Categories
Fastify

Server-Side Development with Fastify — Server Options

Fastify is a small Node framework for developing back end web apps.

In this article, we’ll look at how to create back end apps with Fastify.

Server Options

We can set some options when we require fastify .

http2 lets us use HTTP/2 to bind the socket.

https lets ys listen for TLS connections.

connectionTimeout lets us define the server timeout in milliseconds.

keepAliveTimeout lets us define the server keep-alive timeout in milliseconds.

ignoreTrailingSlash makes Fastify ignore trailing slashes when matching routes.

For example, if we have:

const fastify = require('fastify')({
  ignoreTrailingSlash: true
})

fastify.get('/foo/', function(req, reply) {
  reply.send('foo')
})

// registers both "/bar" and "/bar/"
fastify.get('/bar', function(req, reply) {
  reply.send('bar')
})
const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

The /foo and /foo/ matches the /foo/ route.

And /bar and /bar/ matches the /bar/ route.

maxParamLength sets the max length of the route parameter. Anything longer than the length won’t be invoked.

bodyLimit sets the max request body size.

logger lets us enable the logger.

We can add pino as a custom logger by writing:

const pino = require('pino')();

const customLogger = {
  info(o, ...n) { },
  warn(o, ...n) { },
  error(o, ...n) { },
  fatal(o, ...n) { },
  trace(o, ...n) { },
  debug(o, ...n) { },
  child() {
    const child = Object.create(this);
    child.pino = pino.child(...arguments);
    return child;
  },
};

const fastify = require('fastify')({
  logger: customLogger
})

fastify.get('/foo/', function(req, reply) {
  reply.send('foo')
})

// registers both "/bar" and "/bar/"
fastify.get('/bar', function(req, reply) {
  reply.send('bar')
})
const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

disableRequestLogging lets us disable logging of requests.

serverFactory lets us pass a custom HTTP server to Fastify.

For example, we can write:

const Fastify = require('fastify')
const http = require('http')

const serverFactory = (handler, opts) => {
  const server = http.createServer((req, res) => {
    handler(req, res)
  })
  return server
}

const fastify = Fastify({ serverFactory })

fastify.get('/', (req, reply) => {
  reply.send({ hello: 'world' })
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

to create a server with the http module.

caseSensitive lets us set whether to match routes in a case sensitive manner.

For example, if we have:

const fastify = require('fastify')({
  caseSensitive: false
})

fastify.get('/user/:username', (request, reply) => {
  reply.send(request.params.username)
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

Then if we go to /USER/NODEJS , we see NODEJS since caseSensitive is false .

The genReqId option lets us generate the request ID with our own function,.

For example, we can write:

let i = 0
const fastify = require('fastify')({
  genReqId (req) { return i++ }
})

fastify.get('/', (request, reply) => {
  reply.send('hello world')
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

to create our own function to generate the request ID.

Conclusion

We can configure a few options with our Fastify server.

Categories
Fastify

Getting Started with Server-Side Development with Fastify

Fastify is a small Node framework for developing back end web apps.

In this article, we’ll look at how to create back end apps with Fastify.

Installation

We install the fastify package by running:

npm i fastify --save

with NPM or:

yarn add fastify

with Yarn.

First App

We create a simple server app with a few lines of code.

For example, we can write:

const fastify = require('fastify')({
  logger: true
})

fastify.get('/', (request, reply) => {
  reply.send({ hello: 'world' })
})

fastify.listen(3000, '0.0.0.0', (err, address) => {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
  fastify.log.info(`server listening on ${address}`)
})

to create a simple server with the / route.

logger set to true enables the built-in logger.

reply.send sends a response.

fastify.listen starts the app and listens to the server.

The first argument is the port, the 2nd argument is the IP address to listen for requests from. '0.0.0.0' listens to all addresses.

When we go to / , we see:

{"hello":"world"}

returned.

We can also use astbc and await in our router handlers.

For example, we can write:

const fastify = require('fastify')({
  logger: true
})

fastify.get('/', async (request, reply) => {
  return { hello: 'world' }
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

We just return the object we want as the response.

And we call fastify.listen without the callback.

Validate Request Data

We can validate request data by passing in an option to our route.

For example, we can write:

const fastify = require('fastify')({
  logger: true
})

const opts = {
  schema: {
    body: {
      type: 'object',
      properties: {
        foo: { type: 'string' },
        bar: { type: 'number' }
      }
    }
  }
}

fastify.post('/', opts, async (request, reply) => {
  return { hello: 'world' }
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

to create our schema and then pass in the properties into it.

We specify the types of the foo and bar properties.

This lets us specify the properties that are accepted.

Serializing Data

We can also specify the schema for the response.

For example, we can write:

const fastify = require('fastify')({
  logger: true
})

const opts = {
  schema: {
    response: {
      200: {
        type: 'object',
        properties: {
          hello: { type: 'string' }
        }
      }
    }
  }
}

fastify.post('/', opts, async (request, reply) => {
  return { hello: 'world' }
})

const start = async () => {
  try {
    await fastify.listen(3000, '0.0.0.0')
  } catch (err) {
    fastify.log.error(err)
    process.exit(1)
  }
}
start()

to specify the response that’s returned by the / route.

Run Our Server

We can run our Fastify app with the Fastify CLI.

We install it by running:

npm i fastify-cli

Then in package.json , we add:

{
  "scripts": {
    "start": "fastify start server.js"
  }
}

Then we run it with:

npm start

Conclusion

We can create a simple back end Node web app easily with Fastify.

Categories
JavaScript APIs

IndexedDB Manipulation with Dexie — Sorting, Iteration, and Keys

IndexedDB is a way to store data in the browser.

It lets us store larger amounts of data than local storage in an asynchronous way.

Dexie makes working with IndexedDB easier.

In this article, we’ll take a look at how to start working with IndexedDB with Dexie.

Reverse the Order of Collection Results

We can reverse the order of the collection results with the reverse method.

For example, we can write:

const db = new Dexie("friends");
db.version(1).stores({
  friends: "id, name, age"
});
(async () => {
  await db.friends.put({
    id: 1,
    name: "jane",
    age: 78
  });
  await db.friends.put({
    id: 2,
    name: "mary",
    age: 76
  });
  const friends = await db.friends
    .toCollection()
    .reverse()
    .toArray()
  console.log(friends)
})()

to get all the items and then reverse the order that’s returned.

Sort By the Given Field

We can call the sortBy method to sort by the given field.

For example, we can write:

const db = new Dexie("friends");
db.version(1).stores({
  friends: "id, name, age"
});
(async () => {
  await db.friends.put({
    id: 1,
    name: "jane",
    age: 78
  });
  await db.friends.put({
    id: 2,
    name: "mary",
    age: 76
  });
  const friends = await db.friends
    .where('age')
    .above(25)
    .sortBy('age');
  console.log(friends)
})()

We sort the results by age with the sortBy method.

Convert Collection to an Array

We can convert a collection to an array with the toArray method.

For instance, we can write:

const db = new Dexie("friends");
db.version(1).stores({
  friends: "id, name, age"
});
(async () => {
  await db.friends.put({
    id: 1,
    name: "jane",
    age: 78
  });
  await db.friends.put({
    id: 2,
    name: "mary",
    age: 76
  });
  const friends = await db.friends
    .where('age')
    .above(25)
    .toArray();
  console.log(friends)
})()

We convert the collection returned by the query methods to a promise with an array with the toArray method.

Get Unique Keys

We can get the unique keys from a collection with the uniqueKeys method.

For instance, we can write:

const db = new Dexie("friends");
db.version(1).stores({
  friends: "id, name, age"
});
(async () => {
  await db.friends.put({
    id: 1,
    name: "jane",
    age: 78
  });
  await db.friends.put({
    id: 2,
    name: "mary",
    age: 76
  });
  const keys = await db.friends
    .toCollection()
    .uniqueKeys()
  console.log(keys)
})()

We call uniqueKeys to get the unique keys from the collection.

We get the id values from the collection.

So we get:

[1, 2]

from the console log.

Stop Iterating Collection Once Given Filter Returns true

We can call the until method to stop iterating a collection once a given filter is true .

For instance, we can use it by writing:

const db = new Dexie("friends");
let cancelled = false;

db.version(1).stores({
  friends: "id, name, age"
});
(async () => {
  await db.friends.put({
    id: 1,
    name: "jane",
    age: 78
  });
  await db.friends.put({
    id: 2,
    name: "mary",
    age: 76
  });
  await db.friends
    .toCollection()
    .until(() => cancelled)
    .each(friend => {
      console.log(friend)
      cancelled = true;
    });
})()

We call until with a callback that returns the value of cancelled .

Then we call each and log the value of friend then we cancelled to true .

So after the first iteration of each , it’ll stop looping through the rest of the collection.

Conclusion

We can sort items, convert items to an array, get unique keys, and stop iteration with the given condition with Dexie.

Categories
JavaScript APIs

IndexedDB Manipulation with Dexie — Offsets, Or, and Raw Results

IndexedDB is a way to store data in the browser.

It lets us store larger amounts of data than local storage in an asynchronous way.

Dexie makes working with IndexedDB easier.

In this article, we’ll take a look at how to start working with IndexedDB with Dexie.

Ignore the First n Items Before the Given Offset

We can call the offset method to return the first n items after the given offset.

For instance, we can use it by writing:

const db = new Dexie("friends");  
db.version(1).stores({  
  friends: "id, name, age"  
});  
(async () => {  
  await db.friends.put({  
    id: 1,  
    name: "jane",  
    age: 78  
  });  
  await db.friends.put({  
    id: 2,  
    name: "mary",  
    age: 76  
  });  
  await db.friends  
    .orderBy('name')  
    .offset(10);  
})()

Then we skip the first 10 results in the collection and include the rest.

Logical OR Operation

We can use the or method to combine 2 conditions together with the logical OR operator in the query.

For instance, we can write:

const db = new Dexie("friends");  
db.version(1).stores({  
  friends: "id, name, age"  
});  
(async () => {  
  await db.friends.put({  
    id: 1,  
    name: "jane",  
    age: 78  
  });  
  await db.friends.put({  
    id: 2,  
    name: "mary",  
    age: 76  
  });  
  const someFriends = await db.friends  
    .where("name")  
    .equalsIgnoreCase("jane")  
    .or("age")  
    .above(40)  
    .sortBy("name")  
  console.log(someFriends)  
})()

We call where with the 'name' argument to query by the name.

And we find the entries with the given name with equalsIgnoreCase .

Then we call or to find the items with the age above 40.

And we call sortBy to sort by the name field.

Get an Array Containing All Primary Keys of the Collection

We can use the primaryKeys method to return a promise with an array of primary keys in the collection.

For instance, we can use it by writing:

const db = new Dexie("friends");  
db.version(1).stores({  
  friends: "id, name, age"  
});  
(async () => {  
  await db.friends.put({  
    id: 1,  
    name: "jane",  
    age: 78  
  });  
  await db.friends.put({  
    id: 2,  
    name: "mary",  
    age: 76  
  });  
  const keys = await db.friends  
    .where("name")  
    .equalsIgnoreCase("jane")  
    .primaryKeys()  
  console.log(keys)  
})()

id is the primary key of each entry.

Then we call primaryKeys to return a promise with the array of primary keys.

Get Raw Results

We can get thee raw results from a query with the raw method.

The results won’t be filtered through the reading hooks.

For instance, we can write:

const db = new Dexie("friends");  
db.version(1).stores({  
  friends: "id, name, age"  
});  
(async () => {  
  await db.friends.put({  
    id: 1,  
    name: "jane",  
    age: 78  
  });  
  await db.friends.put({  
    id: 2,  
    name: "mary",  
    age: 76  
  });  
  const keys = await db.friends  
    .where("name")  
    .equalsIgnoreCase("jane")  
    .raw()  
    .toArray()  
  console.log(keys)  
})()

to use it.

Using raw won’t do things like mapping objects to their mapped class.

Conclusion

We can ignore the first n items from the results, get primary keys, and get raw results from an IndexedDB collection with Dexie.

Categories
JavaScript APIs

IndexedDB Manipulation with Dexie — Modifying and Deleting Items

IndexedDB is a way to store data in the browser.

It lets us store larger amounts of data than local storage in an asynchronous way.

Dexie makes working with IndexedDB easier.

In this article, we’ll take a look at how to start working with IndexedDB with Dexie.

Limiting Number of Results Returned

We can limit the number of results returned with the limit method.

For instance, we can write:

const db = new Dexie("friends");
db.version(1).stores({
  friends: "id, name, age"
});
(async () => {
  await db.friends.put({
    id: 1,
    name: "jane",
    age: 78
  });
  await db.friends.put({
    id: 2,
    name: "mary",
    age: 76
  });
  const someFriends = await db.friends
    .orderBy('name')
    .limit(10)
    .toArray()
  console.log(someFriends)
})()

We call limit with 10 as its argument to limit the number of items to 10.

Modifying Results in a Collection

We can modify results in a collection with the modify method.

To use it, we write:

const db = new Dexie("friends");
db.version(1).stores({
  friends: "id, name, age"
});
(async () => {
  await db.friends.put({
    id: 1,
    name: "jane",
    age: 78
  });
  await db.friends.put({
    id: 2,
    name: "mary",
    age: 76
  });
  await db.friends
    .orderBy('name')
    .modify({
      isBigfoot: true
    });
})()

to get the items in the friends table ordered by the name field.

Then we call modify to add the isBigFoot field and set it to true in each item in the collection.

We can also pass in a function by writing:

const db = new Dexie("friends");
db.version(1).stores({
  friends: "id, name, age"
});
(async () => {
  await db.friends.put({
    id: 1,
    name: "jane",
    age: 78
  });
  await db.friends.put({
    id: 2,
    name: "mary",
    age: 76
  });
  await db.friends
    .orderBy('name')
    .modify(friend => {
      friend.age *= 0.5;
    });
})()

We change the age value of each entry in the collection by multiplying it by 0.5.

Also, we can use the 2nd parameter to reference the result entry.

For instance, we can writeL

const db = new Dexie("friends");
db.version(1).stores({
  friends: "id, name, age"
});
(async () => {
  await db.friends.put({
    id: 1,
    name: "jane",
    age: 78
  });
  await db.friends.put({
    id: 2,
    name: "mary",
    age: 76
  });
  await db.friends
    .orderBy('name')
    .modify((friend, ref) => {
      ref.value = {
        ...ref.value,
        age: 55
      }
    });
})()

We set the ref.value to set the value of the entry to something else.

Also, we can use it to delete an object by using the delete operator.

For example, we can write:

const db = new Dexie("friends");
db.version(1).stores({
  friends: "id, name, age"
});
(async () => {
  await db.friends.put({
    id: 1,
    name: "jane",
    age: 78
  });
  await db.friends.put({
    id: 2,
    name: "mary",
    age: 76
  });
  await db.friends
    .orderBy('name')
    .modify((friend, ref) => {
      delete ref.value;
    });
})()

We just delete the ref.value property to delete an item.

The example above is the same as:

const db = new Dexie("friends");
db.version(1).stores({
  friends: "id, name, age"
});
(async () => {
  await db.friends.put({
    id: 1,
    name: "jane",
    age: 78
  });
  await db.friends.put({
    id: 2,
    name: "mary",
    age: 76
  });
  await db.friends
    .orderBy('name')
    .delete();
})()

Conclusion

We can modify and delete items with the collection’s modify method with Dexie.