Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix typos in documentation and modify some parts to improve readability #15

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import { join } from 'desm'
/**
* This is the entry point of our application. As everything in Fastify is a plugin.
* The main reason why the entry point is a plugin as well is that we can easily
* import it in our testing suite and add this application as a subcomponent
* of another Fastify application. The encapsulaton system, of Fastify will make sure
* import it in our testing suite and add this application as a sub component
* of another Fastify application. The encapsulation system, of Fastify will make sure
* that you are not leaking dependencies and business logic.
* For more info, see https://www.fastify.io/docs/latest/Encapsulation/
*/
export default async function (fastify, opts) {
// It's very common to pass secrets and configuration
// to you application via environment variables.
// The `fastify-env` plugin will expose those configuration
// The `fastify-env` plugin will expose those configurations
// under `fastify.config` and validate those at startup.
fastify.register(Env, {
schema: S.object()
Expand All @@ -33,10 +33,10 @@ export default async function (fastify, opts) {
})

// Fastify is an extremely lightweight framework, it does very little for you.
// Every feature you might need, such as cookies or database coonnectors
// Every feature you might need, such as cookies or database connectors
// is provided by external plugins.
// See the list of recognized plugins by the core team! https://www.fastify.io/ecosystem/
// `fastify-sensible` adds many small utilities, such as nice http errors.
// See the list of recognized plugins by the core team! https://www.fastify.io/ecosystem/
// `fastify-sensible` adds many small utilities, such as nice http errors.
fastify.register(Sensible)

// This plugin is especially useful if you expect an high load
Expand All @@ -55,9 +55,10 @@ export default async function (fastify, opts) {
origin: false
})

// Normally you would need to load by hand each plugin. `fastify-autoload` is an utility
// we wrote to solve this specific problems. It loads all the content from the specified
// folder, even the subfolders. Take at look at its documentation, as it's doing a lot more!
// Normally you would need to load each plugin by hand. `fastify-autoload` is an utility
// we wrote to solve this specific problem. It loads all the content from the specified
// folder, even the sub folders. Take at look at its documentation
// https://github.com/fastify/fastify-autoload,as it's doing a lot more!
// First of all, we require all the plugins that we'll need in our application.
fastify.register(AutoLoad, {
dir: join(import.meta.url, 'plugins'),
Expand Down
26 changes: 13 additions & 13 deletions plugins/authorization.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ async function authorization (fastify, opts) {
// so we configure an allow list of emails that we accept.
const allowedUsers = config.ALLOWED_USERS.split(',')

// Undici is an http client for Node.js extremely optimized
// to achieve the best performances possible. Is very well
// Undici is an http client for Node.js, it is extremely optimized
// to achieve the best performances possible. It is very well
// suited if you need to send all the request to the same endpoint.
const client = undici('https://api.github.com')

// `fastify-oauth2` is a plugin that helps you handle oauth2 flows.
// It comes with preconfigured settings for the major oauth providers.
// It comes with pre configured settings for the major oauth providers.
// Are you using Auth0? See https://npm.im/fastify-auth0-verify
fastify.register(OAuth, {
name: 'github',
Expand Down Expand Up @@ -61,10 +61,10 @@ async function authorization (fastify, opts) {
// You can then access your decorator with `fastify.nameOfTheDecorator`.
// See https://www.fastify.io/docs/latest/Decorators/
// Testing authentication flow is hard, especially if you are using OAuth flow.
// As workarond we are using mocks instead of the production authorization utilities.
// As workaround we are using mocks instead of the production authorization utilities.
fastify.decorate('authorize', authorize)
fastify.decorate('isUserAllowed', opts.testing ? isUserAllowedMock : isUserAllowed)
// `decorateRequest` works in the same way of `decorate`, but it changes
// `decorateRequest` works in the same way as `decorate`, but it changes
// the Fastify request object instead. It's very useful if you know you need
// to add properties to the request object, as behind the scenes Fastify will
// optimize the code for you.
Expand All @@ -81,12 +81,12 @@ async function authorization (fastify, opts) {
// and lower the barrier of entry.
//
// The authorization hook will look at the cookies and extract the session
// cookie, in this case `user_session`. If it is not present, throws an error
// cookie, in this case `user_session`. If it is not present, it throws an error
// with `httpErrors.unauthorized`, a utility added by `fastify-sensible`.
// If the cookie is present it will try to unsign it and finally
// If the cookie is present it will try to un-sign it and finally
// verify with the OAuth provider if the provided token is valid
// and is part of the allowed users list.
// If the user is accepeted, it will update the `request.user` property
// If the user is accepted, it will update the `request.user` property
// with the mail address of the user.
async function authorize (req, reply) {
const { user_session } = req.cookies
Expand All @@ -113,7 +113,7 @@ async function authorization (fastify, opts) {

// You can add any property to the request/reply objects,
// but it's important you declare them in advance with decorators.
// If you don't, your code will likely be deoptimized by V8.
// If you don't, your code will likely be de-optimized by V8.
req.user = { mail }
}

Expand Down Expand Up @@ -154,7 +154,7 @@ async function authorization (fastify, opts) {

// Mocks are double edges swords. The main issue with mocks
// is that usually you only test for the success case.
// You should test for the bad case as well, so if you are
// You should test for the failure case as well, so if you are
// writing a mock be sure to handle the failure cases as well.
async function isUserAllowedMock (token) {
if (token === 'invalid') {
Expand All @@ -168,9 +168,9 @@ async function authorization (fastify, opts) {
// in other parts of your application, use `fastify-plugin` to tell Fastify that
// this plugin should not be encapsulated. See https://www.fastify.io/docs/latest/Encapsulation/.
export default fp(authorization, {
// Protip: if you name your plugins, the stack trace in case of errors
// will be easier to read and other plugins can declare their dependency
// on this one. `fastify-autoload` will take care of loading the plugins
// Protip: name your plugins, in the case of errors, the stack trace will display
// those names. It will be easier to read and other plugins can declare their
// dependency on yours. `fastify-autoload` will take care of loading the plugins
// in the correct order.
name: 'authorization'
})
29 changes: 18 additions & 11 deletions plugins/elasticsearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import fp from 'fastify-plugin'
import { Client } from '@elastic/elasticsearch'

/**
* A tipical usage of a plugin, is to expose an external service,
* A typical usage of a plugin, is to expose an external service,
* such as a database. In this case we are exposing the Elasticsearch client
* since the application uses elasticsearch as main datastore.
* since the application uses elasticsearch as a main datastore.
* As you will see, inside this plugin we are not only exposing the
* Elasticsearch instance, but also initializing the necessary indices.
*
* Protip: A rule of thumb to follow is that every plugin should be self-contained,
* and initialize all of its resources before telling to Fastify that is ready to go.
* By design, Fastify won't start listeing to incoming requests if all the registered
* and initialize all of its resources before telling Fastify that is ready to go.
* By design, Fastify won't start listening to incoming requests until all the registered
* plugins have finished their loading. Fastify guarantees the loading order
* thanks to an internal graph structure, provided by https://github.com/fastify/avvio.
*/
Expand All @@ -36,15 +36,15 @@ async function elasticsearch (fastify, opts) {

const indices = {
SHORTURL: 'fastify-app-shortened-url',
// The rate limit index contains all the ip addresses the users that
// sed a request to the application. Given that this index can grown
// The rate limit index contains all the ip addresses that the users
// send as requests to the application. Given that this index can grow
// in size very quickly, a good approach would be to create a new index daily
// and configure Elasticsearch to delete the old ones with an ILM policy.
// See https://www.elastic.co/guide/en/elasticsearch/reference/current/index-lifecycle-management.html
RATELIMIT: 'fastify-app-rate-limit'
}

// We ping the cluster before telling to Fastfy
// We ping the cluster before telling Fastfy
// that the plugin is ready. If the ping operation
// fails, the plugin will throw an exception and the
// application won't start.
Expand All @@ -62,15 +62,22 @@ async function elasticsearch (fastify, opts) {
// need to update the entire application.
fastify.decorate('indices', indices)

// FIFO stands for "first in, first out" and assumes the
// first items in the list are the first ones
// you want. LIFO, also known as "last in, first out," assumes
// the most recent items entered into your list will be
// the ones out.
//
// Often, you need to disconnect from an external service
// when you are shutting down the application, this
// can be done via the `onClose` hook.
// https://www.fastify.io/docs/latest/Hooks/#onclose
// A cool feature of the onClose hook is that as opposed to
// every other hook, which are executed in a FIFO fashion,
// onClose is executed in LIFO.
// This guarantees that your onClose code depending
// on other onClose will be executed first,
//
// This guarantees that your onClose code, depending
// on other onClose functions will be executed first,
// avoiding annoying ordering issues.
fastify.addHook('onClose', (instance, done) => {
instance.elastic.close(done)
Expand All @@ -94,7 +101,7 @@ async function configureIndices (client, indices) {
source: {
type: 'text',
// source is defined a text, which cannot be sorted by Elasticsearch.
// To solve this we used the multi-fields feaure:
// To solve this we used the multi-fields feature:
// https://www.elastic.co/guide/en/elasticsearch/reference/current/multi-fields.html
fields: {
raw: { type: 'keyword' }
Expand All @@ -104,7 +111,7 @@ async function configureIndices (client, indices) {
destination: { type: 'text' },
// should the source appear in the 404 suggestions?
isPrivate: { type: 'boolean' },
// how many times a shot url has been used
// how many times a short url has been used
count: { type: 'integer' },
// who has created the short url
user: { type: 'keyword' },
Expand Down
6 changes: 3 additions & 3 deletions plugins/rate-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ async function rateLimit (fastify, opts) {

// This method will be called each time a new request comes in.
// To avoid sending too many request to Elasticsearch, we'll
// use the update API, which allow us to update the document
// use the update API, which allows us to update the document
// if present, create it if it doesn't exists and read the
// updated document. In this way we'll send up to 2 requests
// to Elasticsearch for each `incr` call, but only one in case
Expand Down Expand Up @@ -77,7 +77,7 @@ async function rateLimit (fastify, opts) {
}
}

// `fastify-rate-limit` does not allow to configure a custom response
// `fastify-rate-limit` does not allow us to configure a custom response
// that is not a JSON, which means that if we want to send something
// different, say an html page, we need to intercept the error and
// change the response. This can be achieved by using the `.setErrorHandler`
Expand All @@ -96,7 +96,7 @@ async function rateLimit (fastify, opts) {
}

// Finally register the rate limit plugin
// and add the needd configuration.
// and add the needed configuration.
fastify.register(RateLimit, {
allowList: ['127.0.0.1'], // no rate limit in our machine
store: ElasticsearchStore,
Expand Down
2 changes: 1 addition & 1 deletion plugins/swagger.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ async function swaggerGenerator (fastify, opts) {
},
// let's expose the documentation only in development
// it's up to you decide who should see this page,
// but it's alwaysx better to start safe.
// but it's always better to start safe.
exposeRoute: fastify.config.NODE_ENV !== 'production'
})
}
Expand Down
6 changes: 3 additions & 3 deletions plugins/validUrl.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import https from 'https'

/**
* This application is an url shortener, and we want to be sure that
* a destination url exists before to store it in Elasticsearch.
* a destination url exists before storing it in Elasticsearch.
* This plugins adds a single `isValidUrl` decorator that performs
* an http request the the destination url to figure out if the url is valid.
* an http request to the the destination url to figure out if the url is valid.
* We are not using `undici` as client (like in the authorization plugin)
* because undici is best suiited for calling the same endpoint multiple
* because undici is best suited for calling the same endpoint multiple
* times, while the Node.js core http client can talk with multiple
* addresses more easily.
*/
Expand Down
19 changes: 9 additions & 10 deletions routes/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default async function admin (fastify, opts) {
fastify.addHook('onRequest', authorize)

// The frontend needs the CSRF token to be able to
// communicate with the others API. This route returns
// communicate with the other APIs. This route returns
// a token for every authenticated request.
fastify.route({
method: 'GET',
Expand Down Expand Up @@ -76,10 +76,10 @@ export default async function admin (fastify, opts) {
schema: {
description: 'Adds a new redirect',
// We do expect a body from the frontend which contains everything
// we need to create a new redirect. Here we define how the body
// we need to create a new redirect. Here we define what the body
// should look like, so we can trust it once we start working
// with it in our route handler. This is not the right place for
// checking user permission tho, that should be done with an hook.
// checking user permission though, that should be done with a hook.
// (`preValidation` should do the job)
body: S.object()
.prop('source', S.string().required())
Expand All @@ -102,9 +102,8 @@ export default async function admin (fastify, opts) {
// responses. You can correlate different logs via their request id.
// https://www.fastify.io/docs/latest/Logging/
// A good question you can ask yourself is: "how much should I log?".
// Is not an easy answer, it dependes on how many information you want
// to know per each request, but remember that the more you log the more
// you pay in terms of storage.
// It is not an easy question, it depends on how much information you want.
// The more you log, the more you pay in terms of storage.
// If you want to send your logs to Elasticsearch, consider using
// https://www.elastic.co/guide/en/ecs-logging/nodejs/current/pino.html
// For shipping your logs, you can either use https://github.com/pinojs/pino-elasticsearch
Expand Down Expand Up @@ -133,7 +132,7 @@ export default async function admin (fastify, opts) {
}
}, {
// ignore is a cool option of the Elasticsearch client,
// it tell to the client not to throw in case of the specified
// it tells the client not to throw in case of the specified
// error, so you can handle it easily without the need of a try catch block.
ignore: [409]
})
Expand All @@ -143,7 +142,7 @@ export default async function admin (fastify, opts) {
}

// Hey! we created a new resource, let's use
// the appropriate status code. And rememeber
// the appropriate status code. And remember
// to configure the response schema as well.
reply.code(201)
return { created: true }
Expand Down Expand Up @@ -235,7 +234,7 @@ export default async function admin (fastify, opts) {
onRequest: csrfProtection,
schema: {
description: 'Get all the redirects defined by the current user.',
// Note that the schema describes how the object will look
// Note that the schema describes what the object will look
// like in your code and not how the user sent it.
// The query can be accessed via `request.query` and it's represented
// by an object. The same can apply if a request body is encoded
Expand Down Expand Up @@ -270,7 +269,7 @@ export default async function admin (fastify, opts) {
term: { user: req.user.mail }
},
// source is defined a text, which cannot be sorted by Elasticsearch.
// To solve this we used the multi-fields feaure:
// To solve this we used the multi-fields feature:
// https://www.elastic.co/guide/en/elasticsearch/reference/current/multi-fields.html
sort: 'source.raw'
}
Expand Down
8 changes: 4 additions & 4 deletions routes/frontend.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ export const autoPrefix = '/_app'
* the application, it registers additional plugins to handle
* static file serving and helmet security headers.
* As you saw in the Elasticsearch plugin, a good rule of thumb is that
* every plugin should be self-contained, reason why we are registering
* the two additonal plugins here and not in the global plugins folder.
* every plugin should be self-contained, this is the reason why we are
* registering two additional plugins here and not in the global plugins folder.
*/
export default async function frontend (fastify, opts) {
// `fastify-helmet` helps you secure your application
// with important security headers. It's not a silver bullet™,
// but security is an orchestraton of multiple tools that work
// but security is an orchestration of multiple tools that work
// together to reduce the attack surface of your application.
fastify.register(Helmet, {
// Here we are telling to the browser to only
// Here we are telling the browser to only
// accept content from the following sources.
contentSecurityPolicy: {
directives: {
Expand Down
8 changes: 4 additions & 4 deletions routes/redirect/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import UpdateCount from './update-count.js'
* Normally if a url shortener doesn't find a matching source,
* it will return a 404. To make it more interesting, we'll use
* the full text search capabilities of Elasticsearch and try to
* suggest a url to he user in case of a typo (à la Google).
* suggest a url to the user in case of a typo (à la Google).
* Wait! Let's do something more, the UI that will suggest alternative
* urls or show the 404 page will be server rendered wth Svelte (https://svelte.dev/).
* urls or show the 404 page will be server rendered with Svelte (https://svelte.dev/).
* As you saw in the Elasticsearch plugin, a good rule of thumb is that
* every plugin should be self-contained, reason why we are registering
* the an additonal plugin here and not in the global plugins folder.
* every plugin should be self-contained, this is the reason why we are registering
* the additional plugin here and not in the global plugins folder.
*/
export default async function short (fastify, opts) {
const {
Expand Down
Loading