Flutter Error: The argument type ‘String’ can’t be assigned to the parameter type ‘Uri’

The Error

If you are using a string URI when dealing with the http package in Flutter, you may be seeing this error:

The Error
The argument type 'String' can't be assigned to the parameter type 'Uri' at .... (argument_type_not_assignable)

This error is due to an update in the package.

The Solution

Parse the String to be an explicit URI by using the Uri.parse() method:

http.get(yourString) becomes http.get(Uri.parse(yourString))

http.post(yourString) becomes http.post(Uri.parse(yourString))

Here is it in an example:

String dataURL = "https://api.com/users";
http.Response response = await http.get(Uri.parse(dataURL));

To improve compile-time type safety, the http package (version 0.13.0) introduced changes that made all functions that previously accepted Uris or Strings now accept only Uris instead.

You will need to explicitly use Uri.parse to convert Strings to Uris. In the previous version, the http packaged called that for you behind the scenes.

How to Use MediaStreams in React

Do you need to access the user’s webcam for video chat or the user’s microphone for a recording? In this simple tutorial, I’ll show you how to access and use the computer’s media inputs with React.

The MediaDevices interface provides access to connected media input devices like cameras and microphones.

Get access to user’s media input

After getting the user’s permission, the MediaDevices.getUserMedia() method produces a MediaStream. This stream can have multiple tracks containing the requested types of media. Examples of tracks include video and audio tracks.

The getUserMedia() method takes in a constraints parameter with two members: video and audio, describing any configuration settings for the tracks. It returns a Promise that resolves to a MediaStream object. You can set your video element’s src to the stream.

// get the user's media stream
    const startStream = async () => {
        let newStream = await navigator.mediaDevices
          .getUserMedia({
            video: true,
            audio: true,
          })
          .then((newStream) => {
            webcamVideo.current.srcObject = newStream;
            setStream(stream);
          });

        setPlaying(true);
    };

Here are some examples of preferences that you can customize in the stream:

// Requests default video and audio
{ audio: true, video: true }

// Requests video with a preference for 1280x720 camera resolution. No audio
{
  audio: false,
  video: { width: 1280, height: 720 }
}

// Requires minimum resolution of 1280x720
{
  audio: true,
  video: {
    width: { min: 1280 },
    height: { min: 720 }
  }
}

// Browser will try to get as close to ideal as possible
{
  audio: true,
  video: {
    width: { min: 1024, ideal: 1280, max: 1920 },
    height: { min: 576, ideal: 720, max: 1080 }
  }
}

// On mobile, browser will prefer front camera 
{ audio: true, video: { facingMode: "user" } }

// On mobile, browser will prefer rear camera
{ audio: true, video: { facingMode: { exact: "environment" } } }

Save user’s media stream in a variable

After you get the user’s media stream from .getUserMedia(), you should save the stream in a state variable. This is so that you can manipulate the stream later (to stop it, get a track from it, etc.)

For example, if you want to stop the stream, get all of the stream’s tracks using the MediaStream.getTracks() method and call the .stop() method on them.

If you want to access the audio separately, use the MediaStream.getAudioTracks() method. To access video separately, use MediaStream.getVideoTracks().

You should also have state that controls if media input is on or off. You should use the useRef hook to control the video element in the DOM.

This is the final code:

import React, { useState, useRef } from 'react';

const App = () => {
    // controls if media input is on or off
    const [playing, setPlaying] = useState(false);

    // controls the current stream value
    const [stream, setStream] = useState(null);
    
    // controls if audio/video is on or off (seperately from each other)
    const [audio, setAudio] = useState(true);
    const [video, setVideo] = useState(true);

    // controls the video DOM element
    const webcamVideo = useRef();

    // get the user's media stream
    const startStream = async () => {
        let newStream = await navigator.mediaDevices
          .getUserMedia({
            video: true,
            audio: true,
          })
          .then((newStream) => {
            webcamVideo.current.srcObject = newStream;
            setStream(stream);
          });

        setPlaying(true);
    };

    // stops the user's media stream
    const stopStream = () => {
        stream.getTracks().forEach((track) => track.stop());

        setPlaying(false);
    };
    
    // enable/disable audio tracks in the media stream
    const toggleAudio = () => {
        setAudio(!audio);
        stream.getAudioTracks()[0].enabled = audio;
    };

    // enable/disable video tracks in the media stream
    const toggleVideo = () => {
       setVideo(!video);
       stream.getVideoTracks()[0].enabled = !video;
    };

    return (
      <div className="container">
	 <video ref={localVideo} autoPlay playsInline></video>
	 <button
	    onClick={playing ? stopStream : startStream}>
	    Start webcam
	 </button>

	 <button onClick={toggleAudio}>Toggle Sound</button>
	 <button onClick={toggleVideo}>Toggle Video</button>
      </div>
    );
}

export default App;

How to Format on Save in VSCode

If format on save was previously not working for you, this is what fixed it for me.

  1. Install Prettier in VSCode (https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
  2. Open Settings pane (Command + ,)
  3. Make sure the Editor: Format on Save property is enabled (You can search and find the property in the search bar)
  4. Set the Editor: Default Formatter to Prettier – Code formatter

Next.js Global State w/ React Context API

The easiest way to implement global state management in your Next.js application is the React Context API.

I made a new file called ContextProvider.js and created a Context object called MyContext:

import React, { useState, useContext, useEffect } from 'react';

const MyContext = React.createContext();

export function useMyContext() {
    return useContext(MyContext);
}

export function MyProvider({ children }) {
    const [myValue, setMyValue] = useState(false);

    return (
        <MyContext.Provider value={{myValue, setMyValue}}>
            {children}
        </MyContext.Provider>
    );
}

Once this is done, go back to pages/_app.js and wrap your component with the context provider:

import { MyProvider } from '../contexts/MyProvider';

function MyApp({ Component, pageProps }) {
    return (
        <MyProvider>
            <Component {...pageProps} />
        </MyProvider>
    );
}

export default MyApp;

Now I can use the state within MyContext in every part of the application.

Make sure to be careful about how much you put into Context. You don’t want unnecessary re-renders across pages when you could just share them across specific components.

Choosing the Best Type of Website for You

There are different types of websites/web apps that come with their own advantages and tradeoffs. The main categories of websites include:

  1. Static websites
  2. Single page applications (SPAs)
  3. Server side rendering (SSR)
  4. Static site generator

When choosing which type of website is best for your needs, you need to look at many factors including SEO, speed, ease of maintenance, technical skill, hosting, and more.

In this article, I will be comparing these four different types of websites based on SEO, speed, ease of maintenance.

SEO (Search Engine Optimization) relates to the process of increasing the traffic to a website from search engines.

Speed refers to how fast your website loads upon initial request and on any subsequent page requests.

Ease of maintenance deals with how convenient it is to update your code; is it modular or repeated?

Static websites

Static websites are made up of static HTML pages (which may include JavaScript and CSS).

Static simply means the site is not generated on the server. Instead of being rendered dynamically, the HTML (and CSS/JS) files just sit on the server waiting to be sent off to a client.

These website pages are uploaded to a CDN / web host. To view a new page on the site, a new request needs to be made to the server.

Static websites have good SEO because web crawlers can view all of the HTML content since the fully populated pages are sent from the server.

On the other hand, static sites are more annoying to update because they require more code re-writing. For example, if you want to change the design of a navigation bar used on all pages, you must manually update it on each page.

These sites can also be slow when navigating to a new page because you need to make a new request to the server each time.

These websites also are not the best at handling and displaying dynamic data.

Single Page Application (SPA)

A single page application is a web application that loads only a single page and updates the body content dynamically using client-side JavaSCript.

Only a single server request is made for an initial mostly-empty HTML page. The pages are populated with content using JS on the client side.

Some examples of SPA frameworks are React or Vue. These frameworks control all of the content, pages, and data fetching from the browser, not the server.

SPAs are faster than traditional static sites because SPAs only require 1 server request.

SPAs utilize component-driven design which makes updating the UI easier because you only have to update it in one place

SPAs are not SEO friendly because the server sends back blank HTML pages.

Server Side Rendered (SSR)

Server side rendered pages are rendered on the server– on the fly– after every page request

When a page request is made, the server first gets any data for the page from a database, then puts that data into a template file, and sends back the resulting HTML page to the browser.

SSR pages are good for SEO and they are easy to re-design because they use templating.

Because a fresh request must be made for every page, these types of sites can be slow.

Static Site Generator

Static Site Generation describes the process of compiling and rendering a website at build time before the site is deployed to the server.

Static pages are compiled at build-time (before deployment)

Before deployment, the SSG framework builds the site and fetches any data that needs to be put into pages. The framework then spits out static pages and a JS bundle that can be deployed to a CDN or static host.

The initial request for the site requires the server to send the files (similar to static site), but the site behaves more like a SPA afterwards: all routing is handled by the JS bundle.

This makes SSGs good for SEO, speed, and updatability.


You will have to decide which type of website is best for you based on the strengths and weaknesses of each type and how important these factors are to the website you’re building.

How to Use Socket.io with Express

socket.io allows us to make a connection between a client and a server but have that connection persist.

Normally, the client and server would have a simple transaction: the client would get data from the server then close the connection. If the client needs 10 pieces of information they have to make 10 requests. With WebSockets, the client can connect to the server and have that connection stay open. So the client can make 10 requests with just 1 connection.

Socket.IO is composed of two parts:

  • A server that integrates with (or mounts on) the Node.JS HTTP Server
  • A client library that loads on the browser side

Set up Socket.io on Express Server

Install dependencies: The only packages you need are Express and Socket.io

npm install express socket.io

Import packages

const http = require('http');
const express = require('express');
const socketio = require('socket.io');

Create io object

const app = express();
const server = http.createServer(app);
const io = socketio(server);

http.createServer is used by Express under the hood but we need to access it directly

Socket.io Methods on the Server

This is a function that runs every time a client connects to our server. In the callback, we create a socket instance for each one of the clients

We can access the socket.id, a random ID that is automatically assigned to them when they join the server.

// Run when client connects
io.on('connection', socket => {
   console.log('New WS Connection...');
});

socket.on listens for events from the client

The string "sendMessage" is a custom event emitted from the client whenever a new message is sent. socket.on allows us to access that event and any data sent with it (message) on the server.

socket.on("sendMessage", message => {
   console.log(message)
})

io.emit sends an event down to every client connected to the server

In this example, we are taking the message that was sent to the server with "sendMessage" and emitting it to all the connected sockets through an event called "recieveMessage".

socket.on("sendMessage", message => {
   io.emit("recieveMessage", message)
})

In order to send a message to every socket except the socket sending the message, use socket.broadcast.emit

socket.broadcast.emit("recieveMessage", message)

Emit message to a room with socket.to in front of the emit command

socket.on("sendMessage", (message, room) => {
   socket.to(room).emit("receiveMessage", message)
})

To join a room, use the socket.join method. You can only join a room from the server, not the client.

socket.on("joinRoom", room => {
   socket.join(room)
})

Send a callback function to the server.

On the client, pass the callback function as the last parameter.

socket.emit("joinRoom", room, message => {
   displayMessage(message)
})

On the server, use the callback function. Basically, you’re doing this: displayMessage(`Joined ${room}`)

socket.on("joinRoom", (room, cb) => {
   socket.join(room)
   cb(`Joined ${room}`)
})

Disconnect from the server

socket.on('disconnect', () => {
   //...
});

Socket.io on the Client

In this tutorial, we are assuming you are using static HTML pages as the frontend. So no JavaScript frameworks like React or Vue.

If Socket.io is installed in the root directory, you can use the client library in our public files by simply adding this line to your main HTML file

<script src="/socket.io/socket.io.js"></script>

You can create a Socket object with this command:

const socket = io()

If Socket.io is NOT installed in the root directory, you must install the socket.io-client package in your client folder.

npm i socket.io-client

Then, in a JavaScript file you can import io from the library

import { io } from 'socket.io-client'

Then, create a socket on the frontend by calling the io function and passing in the address of the server.

const socket = io('http://localhost:3000')

Also, on the server, make sure you add the following parameters to the require('socket.io') function. This will prevent CORS from blocking the frontend from connecting to the backend. The URL should be the URL that your frontend application is running on.

const io = socketio(server, {
  cors: {
    origin: ['http://localhost:8080']
  }
});

Use socket.on to receive events from the server

// Receive message from server and output it to screen
socket.on('message', (message) => {
  outputMessage(message);
});

// Example 2
socket.on('roomUsers', ({ room, users }) => {
  outputRoomName(room);
  outputUsers(users);
});

Use socket.emit to send events to the server

// Tell server you want to join a chatroom
socket.emit('joinRoom', { username, room });

Serverless Lambda Functions with Netlify

What is Serverless?

Serverless computing refers to a backend architecture in which you are not responsible for maintaining your own server to run server-side code.

Instead, you write functions, upload them to a Serverless computing service (ex. AWS Lambda), and the service will run the functions when called.

The service you are using (ex. AWS Lambda) takes care of all the infrastructure for you. So “Serverless” does not mean no servers are involved. Instead, it means you don’t have to deal with the headache of taking care of the server(s) yourself.

Netlify Lambda

Netlify Lambda is powered by AWS Lambda, but just much simpler.

We are going to be using Netlify Lambda to build a simple application.

Build a simple application

Note: must have Node.js Version 10 or later

1) Create a new React app

2) Install the Netlify CLI

You have 2 options for installing the CLI:

Option 1: Install it globally

npm install netlify-cli -g

Option 2: Install it as a dev dependency in project

npm install netlify-cli --save-dev

3) Create a functions folder and make a new .js file inside

This is where we store all of our Lambda functions

I will call my file sayHello.js

4) Inside the file, write this basic boilerplate

Each JavaScript file holds one Lambda function.

The function must export a handler method with the following general syntax:

exports.handler = async function(context, event) {
}

Netlify provides the event and context parameters when the Serverless function is called.

When you call the Serverless function endpoint, the handler receives an event object.

The context parameter includes info about the context in which the Serverless function was called.

Because async functions return a Promise, Netlify recommends returning a response with HTTP status code instead of allow function to time out

exports.handler = async function(context, event) {
   return {
        statusCode: 200,
        message: JSON.stringify({msg: "Hello world"})
    }
}

5) Create netlify.toml file at the root level of the directory

This is a configuration file on how Netlify will build and deploy the site.

command: the command that starts the dev server

functions: where we store our Serverless functions

[build]
command = 'npm run build'
functions = 'functions'
publish = 'build'

6) Run the project

In the project directory, run the command netlify dev. This starts a local dev server

If you append /.netlify/functions/sayHello to the URL, the server will send the message defined earlier ({msg: “Hello world”})

The full URL:

localhost:4000/.netlify/functions/sayHello

7) Call function in frontend

This is an example of how you could access the data with axios. You would probably use React Hooks to asynchronously get the data and put it in a view.

const results = await axios.get('/.netlify/functions/sayHello')

As you can see, you use a local URL in the GET request

All About Logging

What is an application log?

There are multiple different types of logging including system logs, server logs, garbage collector logs, and application logs.

An application log is a file that contains information about events that have occurred within a software application. Every application produces logs which hold information about what the application is doing.

What type of information is included in a log? It varies by the application. The developers of the software application control what goes into the log file, but there are some commonly included components:

  • context info: Background information that provides insight into the state of the application at the time of the message
  • timestamps: A specific piece of contextual information for tracking and correlating issues
  • Log levels: Labels that help you calculate the level of importance for the entries in your log file. Frequently used levels include INFO, WARN, and ERROR

Why are logs important?

The main use for logs is diagnostics. Logs hold information that can help with

  • debugging
  • analytics
  • compliance
  • security

Logging Agent vs Logging Library

A logging agent is a separate service that reads logs from one location and sends them to another location. Think of it like a funnel.

Some benefits of logging agents include:

  1. minimal setup & config
  2. multi-threading & failure safety
  3. no code
  4. scalable (single agent instance can log for almost any number of applications)
  5. modify/update/replace agents w/o having to take down application
  6. format-agnostic

Some drawbacks of logging agents include:

  1. require interaction w/ host (not good w/ Serverless)
  2. requires CPU compute usage to track log files

A logging library (or logging framework) is code that you embed into your application to create and manage log events.

Some benefits of logging libraries include:

Some drawbacks of logging libraries include:

  • usually synchronous (application must wait for the library to finish writing a log event before continuing)
  • no failure safety (libraries only run as long as your application is running)
  • not scalable (each application runs its own instance of the library)

Logging in Node with Winston

What is Winston?

Winston is a simple and universal logging library.

Why use a logging library?

When developing an application, sometimes we need to get information about how the application is running. We can use functions like console.log, console.error, console.warn to accomplish this. A logging library, such as Winston, allows us to output similar types of data except it can send this data (log data) to locations other than just the console. For example, an external file.

The basic idea of logging

Basically, you have a logger that writes to an arbitrary amount of transports. A transport is essentially a storage device for your logs. Some common transports include:

  • stdout (console)
  • file
  • HTTP
  • Datadog (log aggregator)

Log levels

Log levels allow us to categorize the entries in a log file by urgency. The higher up it is, the more severe it is. As you can see, fatal is the most severe.

These are npm log levels:

{
   fatal: 0,
   error: 0,
   info: 0,
   http: 0,
   verbose: 0,
   debug: 0,
}

Setting up a simple logger in Winston

The recommended way to use winston is to create your own logger. The simplest way to do this is using winston.createLogger.

Install Winston in project

npm install winston

In a new file, create a logger with winston.createLogger

const winston = require('winston');

const logger = winston.createLogger({

});

module.exports = logger;

Add custom properties to logger

const winston = require('winston');

const logger = winston.createLogger({
   format: winston.format.simple(),
   transports: [new winston.transports.Console()],
});

module.exports = logger;

The format property controls how the logs are displayed visually.

transports is an array of the places (transports) to send the log data to. In the above example, our only transport is the console.

Use the logger

const logger = require('./logger')   // access the file you created earlier

logger.info('text info');
logger.warn('text warn');
logger.error('text error');
logger.debug('text debug');

The output should look something like this:

Why is the debug log not showing up? The default log level for a Winston logger is info. This means that Winston will only log the info level and all levels ABOVE it (warn, error, etc.)

Creating custom log format in Winston

Destructure Winston import

This is just for our own ease of use. No change to the logic.

const { createLogger, format, transports } = require('winston');
const { timestamp, combine, printf } = format;

const logger = createLogger({
  format: format.simple(),
  transports: [new transports.Console()]
});

Create custom format

const myFormat = printf(({ level, message, timestamp }) => {
  return `${timestamp} ${level}: ${message}`;
});

Add the format to your logger

const logger = createLogger({
  format: combine(timestamp(), myFormat),
  transports: [new transports.Console()]
});

Final code

const { createLogger, format, transports } = require('winston');

const myFormat = printf(({ level, message, timestamp }) => {
  return `${timestamp} ${level}: ${message}`;
});

const logger = createLogger({
  format: combine(timestamp(), myFormat),
  transports: [new transports.Console()]
});

Here is another custom log format

const logger = createLogger({
  format: combine(
       format.colorize(),
       timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), 
       myFormat
  ),
  transports: [new transports.Console()]
});