Criação de uma API Mocking com MirageJS
Criação de uma API Mocking com MirageJS
Neste artigo, iremos abordar um recurso que todo Dev Front-end gostaria de ter disponível desde o começo do desenvolvimento de uma aplicação Front- end: uma API Mocking para fazer a integração dos dados.
Como todo início do desenvolvimento de uma aplicação web completa (back-end e frontend) nem sempre alguma destas partes estará disponível com antecedência para fazer a integração. Assim, é bastante comum em alguns projetos começarem o desenvolvimento pelo front-end (principalmente em casos onde você estará trabalhando sozinho no projeto), ou os dois ao mesmo tempo.
Dessa forma, como produtividade é muito importante, a equipe de front-end não quer ficar parada. Assim, pode seguir com a implementação com dados estáticos através de arquivos JSON ou armazenado em uma variável até que o back-end comece a fornecer uma API com dados reais que o front-end precise.
Vantagens e Desvantagens da API Mocking
A desvantagem de usar dados estáticos é que pode dificultar um pouco em apresentar cenários de dados. Por exemplo: em um momento podemos ter uma lista de usuários vazia, com 10, com 100, com 1000 e assim por diante e ficar alterando esses valores estaticamente não traz um dinamismo esperado.
A vantagem de utilizar uma API Mocking é que conseguiremos apresentar cenários de forma muito fácil, preparar o formato dos objetos (JSON) com as propriedades esperadas pelo front-end. Assim, o back-end pode reaproveitar e utilizar na criação da API, trazendo agilidade no desenvolvimento. Isso porque ele já saberá como front-end espera que os dados sejam enviados, testar solicitações HTTP (GET, POST, DELETE, PUT e PATCH) e apresentar mensagens de sucesso ou erros destas solicitações.
O que é o MirageJS?
Segundo a própria definição da documentação: “O Mirage JS é uma biblioteca de simulação de API que permite criar, testar e compartilhar um aplicativo JavaScript completo e funcional sem precisar depender de nenhum serviço de back-end”.
Ou seja, o MirageJS é uma lib que cria um servidor local dentro do projeto front-end que irá interceptar as requisições que saem da aplicação front-end e responde com o conteúdo que foi configurado.
Vantagens de utilizar o MirageJS
- Servidor local dentro do próprio projeto. Isso significa que, assim que o servidor de desenvolvimento da aplicação front-end subir, o MirageJS (e o servidor local) irá executar junto;
- Hot Reload: alterações ou adições de novos recursos. Ao salvar, o servidor automaticamente será atualizado e as mudanças serão refletidas na aplicação rodando no navegador;
- Debugar é muito fácil. Como o MirageJS é totalmente construído com Javascript, podemos utilizar a instrução debugger e analisar o ponto especificado pelo o dev-tools do navegador;
- Um mesmo servidor tanto para ambiente de desenvolvimento quanto para ambiente de testes;
- Rotas para lidar com solicitações HTTP;
- Um banco de dados e modelos para armazenar dados e definir relacionamentos;
- ORM embutido;
- Utilização de factories e seeds.
Iniciando um projeto React e configurando o servidor do MirageJS
Sem mais delongas, primeiro vamos implementar uma aplicação simples para registrar e remover usuários, e também enviar requisições HTTP para o servidor local do MirageJS.
Então, primeiro vamos iniciar um projeto React com ViteJS (https://vitejs.dev/guide/#scaffolding-your-first-vite-project). Você pode fazer isso rodando algum dos comandos abaixo:
npm create vite@latest miragejs-demo
// or
yarn create vite miragejs-demo
Para facilitar o entendimento do código, iremos utilizar o React sem Typescript. Entretanto, se você estiver confortável com o TS, fique a vontade para utilizar e fazer as devidas tipagens.
No App.jsx, vamos montar uma estrutura básica com um formulário simples para registrarmos o nome do usuário e listagem.
import { useState } from "react";
function App() {
const [users, setUsers] = useState([]);
return (
<div>
<form>
<p>
<label htmlFor="user-register">Name: </label>
<input
type="text"
id="user-register"
placeholder="Enter username"
name="userName"
/>
</p>
<button type="submit">Register</button>
</form>
<h1>Users:</h1>
<ul>
{(!!users || users.length > 0) &&
users.map((user, index) => <li key={index}>{user.name}</li>)}
</ul>
</div>
);
}
export default App;
Como podemos notar, nossa aplicação inicia com dados de usuários vazio no estado. Dessa forma, para termos algo apresentado em tela inicialmente, iremos criar o servidor do MirageJS e definir a primeira rota GET, que vai retornar alguns usuários para o front-end.
Antes de mais nada, precisamos instalar o MirageJS em nossa aplicação:
# Using Yarn
yarn add -D miragejs
# Using npm
npm install --save-dev miragejs
No main.jsx, vamos criar o servidor com uma rota do tipo GET /users. Então, quando o nosso front-end fizer uma solicitação GET para /api/users, o Mirage responderá com esses dados:
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { createServer } from "miragejs";
import "./index.css";
// Servidor local do MirageJS
createServer({
routes() {
this.namespace = "api";
this.get("/users", () => {
return {
users: [
{ id: 1, name: "John Doe" },
{ id: 2, name: "Jane Doe" },
{ id: 3, name: "Gabe" },
],
};
});
},
});
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Ótimo! Agora, o próximo passo é fazer o front-end enviar uma solicitação para buscar os dados dos usuários:
function App() {
const [isLoading, setIsLoading] = useState(true)
const [users, setUsers] = useState([])
useEffect(() => {
async function fetchUsers () {
const res = await fetch('http://localhost:3000/api/users')
const data = await res.json()
setUsers(data.users)
setIsLoading(false)
}
fetchUsers()
}, [])
if (isLoading) {
return <p>Loading...</p>
}
return (
//... restante do código aqui.
)
E voilà! Assim, nosso front-end agora inicia com informações vindas de uma API funcional.
Ao abrir o console do dev-tools do seu navegador, você conseguirá analisar o request e response interceptado pelo o MirageJS:
Criando models, seeds e rotas para registrar e deletar usuários
Bom, até aqui nossa aplicação já possui uma configuração inicial de uma API Mocking. Apesar de termos um servidor devolvendo informações, o cenário atual ainda é de dados estáticos, já que preenchemos objetos de forma fixa (hardcode) no retorno da rota /api/users e o nosso desejo é de ter algo dinâmico.
Dessa forma, vamos fazer uso da camada de dados do Mirage que vai nos ajudar a escrever uma implementação de servidor mais poderosa.
Primeiro, vamos criar uma model para os users:
createServer({
models: {
user: Model
},
//... restante do código aqui.
)}
Agora, vamos refatorar nossa rota /users para torna-la dinâmica e fazer uso do banco de dados em memoria do Mirage:
// ...restante do código aqui
routes() {
this.namespace = "api";
this.get("/users", (schema, request) => {
return schema.users.all();
}
});
É através do schema que a rota acessará a model user e responderá com todas as informações do banco de dados do Mirage, no momento da solicitação. Assim, se voltamos ao nosso app rodando no navegador, teremos aquele cenário com o estado inicialmente vazio.
Sem problemas! Com seeds, podemos iniciar nosso banco de dados com alguns dados iniciais:
createServer({
//... restante do código aqui
seeds(server) {
server.create("user", { name: "John Doe" });
server.create("user", { name: "Jane Doe" });
server.create("user", { name: "Gabe" });
},
});
Maravilha! Voltamos ao cenário onde temos uma listagem de usuários sendo apresentada em tela. Agora chegou o momento de registrarmos usuários!
Para começar, vamos definir nossa rota do tipo POST para criar/registrar um usuário no banco de dados do Mirage:
createServer({
//... restante do código aqui
routes() {
//... restante do código aqui
this.post("/users", (schema, request) => {
const user = JSON.parse(request.requestBody);
return schema.users.create(user);
});
},
//... restante do código aqui
});
Então, o próximo passo é enviar os dados do usuário do front-end para o servidor:
function App() {
//... restante do código aqui
async function handleSubmit(e) {
e.preventDefault();
setIsLoading(true);
const {
userName: { value: userName },
} = e.currentTarget;
const res = await fetch("https://localhost:3000/api/users", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: userName,
}),
});
const data = await res.json();
const registeredUser = data.user;
setUsers((state) => state.concat(registeredUser));
setIsLoading(false);
}
//... restante do código aqui
return (
<div>
<form onSubmit={handleSubmit}>{/* ... restante do código aqui */}</form>
</div>
);
}
Por isso, o que faremos a seguir é definir a rota para deletar usuários pelo o id enviado via params.
createServer({
//... restante do código aqui
routes() {
//... restante do código aqui
this.delete("/users/:id", (schema, request) => {
const id = request.params.id;
return schema.users.find(id).destroy();
});
},
//... restante do código aqui
});
Agora no App.jsx vamos definir uma função que irá enviar a solicitação para deletar o usuário com base no id:
async function handleDeleteUserById(id) {
setIsLoading(true);
const res = await fetch(`/api/users/${id}`, { method: "DELETE" });
if (res.ok) {
setUsers((state) => state.filter((item) => item.id !== id));
}
setIsLoading(false);
}
Ainda no App.jsx, vamos adicionar um button no JSX que irá disparar/executar a função handleDeleteUserById:
//... restante do código aqui.
<ul>
{(!!users || users.length > 0) &&
users.map((user) => (
<li key={user.id}>
{user.name}
<button onClick={() => handleDeleteUserById(user.id)}>Delete</button>
</li>
))}
</ul>
Agora temos uma configuração no servidor onde ele inicia com alguns dados e fará inserções e remoções de registros de forma dinâmica.
Conclusão
Essa foi uma breve e básica introdução sobre o MirageJS, mostrando seus recursos na prática com uma simples aplicação em React.
A ferramenta é muito mais do que foi mostrado aqui. O Mirage consegue criar uma API REST completa, uma camada de dados poderosa que facilita a manipulação dos dados no banco, seeds, factories, relacionamentos, ORM, testes, entre outros recursos.
Sugiro que visitem a documentação para aprenderem mais sobre a ferramenta, que será muito útil para o desenvolvimento de suas aplicações, principalmente para os devs front-end.
Autor: Gabriel Pinheiro da Silva.