The Developer’s Handbook

Mastering Stubby: The Ultimate Step-by-Step Guide to Effortless Backend Mocking

Unlocking Development Secrets: Your Step-by-Step Guide to Mastering Stubby

Code & Cozy
10 min readJun 9, 2023
by Pexels

When working with front-end technologies like ReactJS, you may encounter situations where you need to incorporate server-side code and complex database queries. This can be pretty overwhelming, especially when your main focus is on the client side. Or maybe you are in a multidisciplinary team with a backend colleague working by your side on the same feature, but what if the back end isn’t ready and you need to test the services? Or you’re a full-stack developer and can do both things, but what if you’re short in time and you need to immediately try the responses of a possible endpoint that is not ready for now? Don’t worry. There’s a solution called stubby4node.

Stubby4node is a fantastic npm package that simplifies the process. It allows you to create a pretend server that acts like a real back end during development. You can easily configure it to respond to incoming requests, mimicking or mocking the behavior of external systems. It’s like having a magic tool that helps you avoid the complexities of setting up a complete back end.

So, embrace stubby4node and let it be your secret weapon for smoother development. Say goodbye to the worries of incomplete back ends and hello to a simplified and engaging front-end development experience.

Today in this post, we will install and implement stubby4node in a ReactJS & NextJS project and REST API in the most easy-to-follow and step-by-step way possible. From the installation to the creation of the services to the initialization of the whole project with stubby using commands.

P.S. You can also check out the GitHub repository where all the code mentioned in this post is available.

#1 Installation

To install the Stubby library in your project, you need to go to the official page and copy the npm command. This command will add the stubby package as a devDependency in your package.json file.

npm install --save-dev stubby

You can find more information about the Stubby library on its npm page.

#2 Configuration

Create a folder named stubby in the main directory of your project. Inside the stubby folder, create a file named index.yaml. This file will contain the configuration for all the endpoints you'll be using in your project.

Here’s an example file structure:

.
├── src
│ ├── components
│ ├── pages
│ └── styles
├── stubby
│ └── index.yaml
├── README.md
├── jsconfig.json
├── next.config.js
├── package-lock.json
└── package.json

You can check the configuration documentation of the Stubby repository for more details.

#3 Create endpoints and responses

Now that you have set up the configuration, you can add your endpoints and their corresponding responses. Create a folder inside the stubby folder. Inside this folder, create sub-folders for each endpoint and add the response data in JSON format.

For example, if you have a GET endpoint to get the user data and a POST endpoint to create a new user, you can create a folder named user and sub-folders named get-user and create-new-user to represent these endpoints.

P.S. You can call it whatever you want, but in order to maintain coherence it needs to be related to the endpoints or the service that you are calling, and also you need to use the same names of the folder and files when you call it after.

Here’s an example file structure:

.
├── src
│ ├── components
│ ├── pages
│ └── styles
├── stubby
│ ├── create-new-user
│ │ └── data.json
│ ├── get-user
│ │ └── data.json
│ └── index.yaml
├── README.md
├── jsconfig.json
├── next.config.js
├── package-lock.json
└── package.json

In each data.json file, you can add the actual response data that you would expect from the backend. For example:

// get-user/data.json
{
"name": "Anita",
"email": "Smith"
}
// create-new-user/data.json
{
"status": 200,
"message": "User created successfully!",
"user": {
"id": 111,
"name": "Anita",
"email": "Smith"
}
}

<aside> 💡 Please note that these endpoints and responses will only work locally and not in a production environment. Make sure to use appropriate endpoints and responses when linking the backend with the frontend of the project.

</aside>

After creating the folders, files, and responses, you need to specify the endpoints and their respective file paths in the index.yaml file. Make sure to start each endpoint ^/ to indicate the beginning of the endpoint URL. Use the same route and file names that you defined in the stubby directory.

Here’s an example index.yaml file:

- request:
method: GET
url: ^/user/get-user
response:
headers:
content-type: application/json
file: ./get-user/data.json
- request:
method: POST
url: ^/user/create-new-user
headers:
content-type: application/json
response:
headers:
content-type: application/json
status: 200
file: ./create-new-user/data.json

#4 Create services

Now we need to call those endpoints and use them in the project. If you have already completed this part, you can skip it and move on to the next step.

To maintain order, it’s preferable to create specific folders for services, which are functions that call the endpoints using the fetch function. These services can be used in components or pages. In this case, we will create a folder called services under the src folder, and inside it, a folder called user with an index.js file. You can choose to organize your functions by creating a folder for each service or by putting all related functions in a single file. In my case, I'll create a single file containing all the services related to the user.

Here’s an example directory structure:

.
├── src
│ ├── components
│ ├── pages
│ ├── services
│ │ └── user
│ │ └── index.js
│ └── styles
├── stubby
│ ├── create-new-user
│ │ └── data.json
│ ├── get-user
│ │ └── data.json
│ └── index.yaml
├── README.md
├── jsconfig.json
├── next.config.js
├── package-lock.json
└── package.json

Next, create the file services/user/index.js and define the functions that call the endpoints. These functions should match the endpoints defined in both the stubby server and the backend.

export const getUser = async (idUser) => {
try {
const url = `/user/get-user?id=${idUser}`;
const response = await fetch(url);
const data = await response.json();
return data;
} catch (error) {
console.error(error.message);
}
};

export const createNewUser = async (userData) => {
try {
const url = "/user/create-new-user";
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(userData),
});
const data = await response.json();
return data;
} catch (error) {
console.error(error.message);
}
};

In this example, we’ll use these functions in a form. When the form is submitted, we’ll call the endpoint that creates a new user in the database (user/create-new-user). If the response has a status code of 200, we'll display a message with the information of the new user using the endpoint /user/get-user.

Here’s an example of using these functions in the Home component:

import { createNewUser, getUser } from '@/services/user';
import Head from 'next/head'
import { useState } from 'react';

const Home = () => {
const [form, setForm] = useState({});

const handleChangeInput = (e) => {
setForm({...form, [e.target.name]: e.target.value});
};

const createMessage = (isError, user) => {
return isError ? 'The user was succesfully created' :
`Hi! ${user.name} your user was succesfully created`
};

const getNewUser = async (idUser) => {
try {
const newUser = await getUser(idUser);
alert(createMessage(false, newUser));
}
catch (error) {
console.error(error.message);
return alert(createMessage(true, {}));
}

};

const handleRegisterSubmit = async (e) => {
e.preventDefault();
try {
const responseRegister = await createNewUser(form);
if(responseRegister.status == 200) getNewUser(responseRegister.user.id);
} catch {
alert('Something happened, please try again')
}
};

return (
<>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<form onSubmit={e => handleRegisterSubmit(e)}>
<div>
<label>Your name</label>
<input name='name' placeholder="John Smith" onChange={e => handleChangeInput(e)} />
</div>
<div>
<label>Your email</label>
<input name='email' placeholder="your_email@email.com" onChange={e => handleChangeInput(e)} />
</div>

<button type='submit'>Register</button>
</form>
</main>
</>
)
};

export default Home;

#5 Initialize stubby

Now that everything has been created and used, let’s configure the package.json file of the project to integrate it with stubby.

First, we need to install the concurrently dependency. This package allows us to manage and run multiple processes simultaneously without the need for separate terminal windows. It will help us run both the stubby server for API mocking and the Next.js server for our frontend code in a single command.

To install concurrently as a devDependency, run the following command:

npm install concurrently --save-dev

For more details, you can check their own documentation.

Now let’s update the package.json file. Add the dev:stubby and stubby scripts alongside the existing Next.js scripts. The dev:stubby script will run both the stubby server and the Next.js development server concurrently, while the stubby script will only start the stubby server.

Here’s an example of the updated package.json:

{
"name": "example-stubby",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"dev:stubby": "concurrently --kill-others \\"npm run stubby\\" \\"npm run dev\\"",
"stubby": "stubby -w -d ./stubby/index.yaml -s 8815 -a 8816 -t 8817"
},
"dependencies": {
"next": "13.4.4",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"concurrently": "^8.2.0"
}
}

Let me explain the purpose of each command.

dev:stubby: This script is used to start both the stubby server and the Next.js development server concurrently. It uses the concurrently package to run multiple commands in parallel within a single terminal window.

npm run stubby: This command runs the stubby script, which starts the stubby server for API mocking based on the configurations specified in the index.yaml file.

npm run dev: This command starts the Next.js development server, which runs your Next.js application.

Combining these two commands using concurrently allows you to run both the stubby server and the Next.js server simultaneously, enabling API mocking while developing your frontend application.

stubby: This script starts the stubby server independently without running the Next.js server.

stubby -w -d ./stubby/index.yaml -s 8815 -a 8816 -t 8817: This command starts the stubby server with the following configurations:

  • -w: Watches the index.yaml file for changes and automatically reloads the server when changes occur.
  • -d ./stubby/index.yaml: Specifies the location of the YAML file that contains the stubby server configurations.
  • -s 8815: Sets the HTTP port for the stubby server.
  • -a 8816: Sets the HTTPS port for the stubby server.
  • -t 8817: Sets the stubby admin portal port.

Running the stubby script alone starts the stubby server, allowing you to use it for API mocking independently of the Next.js server.

These scripts provide convenience and flexibility in running the stubby server and the Next.js server based on your specific needs during development.

If you want to check if stubby was initialized correctly, you can use npm run stubby. In the terminal, you should see something like this:

> example-stubby@0.1.0 stubby
> stubby -w -d ./stubby/index.yaml -s 8815 -a 8816 -t 8817
Loaded: GET ^/user/get-user
Loaded: POST ^/user/create-new-user
Watching for changes in ./stubby/index.yaml...
Quit: ctrl-c
Stubs portal running at <https://0.0.0.0:8817>
Stubs portal running at <http://0.0.0.0:8815>
Admin portal running at <http://0.0.0.0:8816>

This means that stubby is running at the portal http://0.0.0.0:8815. If you want to call the endpoint that gives user information, you can check the URL http://0.0.0.0:8815/user/get-user. It should return the following response and be printed on the page:

{
"name": "Anita",
"email": "Smith"
}

The best part is that now you can use npm run dev:stubby to start both the stubby server and the Next.js development server at the same time, allowing you to test your API endpoints while working on your Next.js project.

But something is missing! If you run the solution, fill out the form, and click the Register button, it will show an alert message indicating that something went wrong. If you check the network tab in the Dev Console of the browser, you will see a 404 status code (Not Found) for the create-new-user endpoint. The reason is that we are calling the endpoint http://localhost:3000/user/create-new-user, but our endpoint is actually using port 8815. So the correct endpoint URL should be http://0.0.0.0:8815/user/create-new-user.

To fix this issue, we need to add an environment variable in the .env file and include it in the Next.js configuration.

Update the .env file with the following content:

DEPLOY_ENVIRONMENT=local
BASE_URL=http://localhost:3000
LOCAL_PORT_BFF=8815

Next, update the next.config.js file. This configuration handles both the local and production environments.

/** @type {import('next').NextConfig} */
const ENV = process.env.DEPLOY_ENVIRONMENT || 'local';
const LOCAL_PORT_BFF = process.env.LOCAL_PORT_BFF || 8080;
const getUrlsByEnvironment = {
local: {
BFF: `http://localhost:${LOCAL_PORT_BFF}`,
},
prod: {
BFF: '<https://www.url-backend.com>',
},
};
const nextConfig = {
reactStrictMode: true,
publicRuntimeConfig: {
...getUrlsByEnvironment[ENV]
}
}
module.exports = nextConfig

With these configurations in place, run the command npm run dev:stubby again, and you will see that the endpoints are now being called correctly.

Remember that the .env file should only contain environment-specific variables for your local environment. When deploying to platforms like Vercel, you will need to set the appropriate environment variables for the production environment.

For more information on environment variables, you can refer to the Vercel documentation on environment variables.

P.S. If you are a native Spanish speaker or learning this beautiful language, you can check out the twin post that will be in Spanish. The same concept and explanations but in another language 😉 every week.

--

--

Code & Cozy

Welcome to Code & Cozy, a tech blog where programming meets warmth and inspiration. Join us on a journey of learning and growth in the world of web development.