IPFS & Filebase
Se você quiser entender um pouco melhor o IPFS, confira o último artigo escrito para entender e fazer upload de arquivos para o IPFS com Filebase. Este tutorial se concentrará na criação de uma API de back-end com Node que permite o upload para IPFS usando Filebase.
Requisitos
Antes de começarmos, precisamos garantir que você tenha o seguinte instalado localmente em seu computador.
- NVM or Node v16.15.1
- Yarn
- Postman (Opcional - também será exibido com curl)
Configuração do projeto Node
O primeiro passo é configurar nosso projeto do zero com Express & Typescript.
mkdir node-filebase-ipfs-uploader;
cd node-filebase-ipfs-uploader;
mkdir bucket; # Pasta que usaremos para testes locais
touch bucket/.gitkeep; # Para garantir que mantemos a pasta e não seu arquivo
mkdir src; # Onde nosso código ficará
echo "16.15.1" > .nvmrc;
nvm install; # ignore se você já tiver o Node 16.15.1 instalado
yarn init -y;
git init;
echo "node_modules\n.env\nbucket/*\n\!bucket/.gitkeep\nbuild\n*.log" > .gitignore;
yarn add aws-sdk cors express dotenv multer multer-s3 typescript @aws-sdk/client-s3 @types/cors @types/express @types/multer-s3 @types/node;
yarn add -D nodemon ts-node;
./node_modules/.bin/tsc --init; # gera nosso arquivo tsconfig.json
Agora que temos nosso projeto configurado, vamos fazer uma modificação em nosso arquivo de configuração TypeScript para ajustar a pasta de saída para ./build
.
Arquivo: ./tsconfig.json
{
"compilerOptions": {
/* Visite https://aka.ms/tsconfig para ler mais sobre este arquivo */
/* Projects */
// "incremental": true, /* Salve arquivos .tsbuildinfo para permitir a compilação incremental de projetos. */
// "composite": true, /* Habilite restrições que permitem que um projeto TypeScript seja usado com referências de projeto. */
// "tsBuildInfoFile": "./.tsbuildinfo", /*Especifique o caminho para o arquivo de compilação incremental .tsbuildinfo. */
// "disableSourceOfProjectReferenceRedirect": true, /* Desative a preferência por arquivos de origem em vez de arquivos de declaração ao fazer referência a projetos compostos. */
// "disableSolutionSearching": true, /* opte por excluir um projeto da verificação de referência de vários projetos durante a edição. */
// "disableReferencedProjectLoad": true, /* Reduza o número de projetos carregados automaticamente pelo TypeScript. */
/* Language and Environment */
"target": "es2016" /*Defina a versão da linguagem JavaScript para o JavaScript emitido e inclua declarações de biblioteca compatíveis. */,
// "lib": [], /* Especifique um conjunto de arquivos de declaração de biblioteca compactados que descrevem o ambiente de tempo de execução de destino. */
// "jsx": "preserve", /* Especifique qual código JSX é gerado.*/
// "experimentalDecorators": true, /* Ative o suporte experimental para decoradores de rascunho do estágio 2 do TC39.*/
// "emitDecoratorMetadata": true, /* Emite metadados de tipo de design para declarações decoradas em arquivos de origem. */
// "jsxFactory": "", /* Especifique a função de fábrica JSX usada ao direcionar a emissão do React JSX, por exemplo 'React.createElement' ou 'h'. */
// "jsxFragmentFactory": "", /* Especifique a referência de fragmento JSX usada para fragmentos ao direcionar a emissão do React JSX, por exemplo 'React.Fragment' ou 'Fragmento'. */
// "jsxImportSource": "", /* Especifique o especificador de módulo usado para importar as funções de fábrica JSX ao usar 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Especifique o objeto chamado para 'createElement'. Isso se aplica apenas ao direcionar a emissão de JSX 'reagir'. */
// "noLib": true, /* Desative a inclusão de quaisquer arquivos de biblioteca, incluindo o padrão lib.d.ts. */
// "useDefineForClassFields": true, /* Emita campos de classe compatíveis com o padrão ECMAScript.*/
// "moduleDetection": "auto", /* Controle qual método é usado para detectar arquivos JS em formato de módulo. */
/* Modules */
"module": "commonjs" /* Especifique qual código de módulo é gerado. */,
// "rootDir": "./", /* Especifique a pasta raiz em seus arquivos de origem.*/
"moduleResolution": "node" /* Especifique como o TypeScript procura um arquivo de um determinado especificador de módulo. */,
// "baseUrl": "./", /* Especifique o diretório base para resolver nomes de módulos não relativos. */
// "paths": {}, /* Especifique um conjunto de entradas que mapeiam novamente as importações para locais de pesquisa adicionais. */
// "rootDirs": [], /* Permita que várias pastas sejam tratadas como uma ao resolver módulos. */
// "typeRoots": [], /* Especifique várias pastas que agem como './node_modules/@types'. */
// "types": [], /* Especifique nomes de pacotes de tipo a serem incluídos sem serem referenciados em um arquivo de origem. */
// "allowUmdGlobalAccess": true, /* Permita o acesso a globais UMD a partir de módulos. */
// "moduleSuffixes": [], /* Lista de sufixos de nome de arquivo para pesquisar ao resolver um módulo.*/
// "resolveJsonModule": true, /* Ative a importação de arquivos .json. */
// "noResolve": true, /* Não permita que 'import's, 'require's ou '<reference>'s expandam o número de arquivos que o TypeScript deve adicionar a um projeto. */
/* JavaScript Support */
// "allowJs": true, /* Permita que arquivos JavaScript façam parte do seu programa. Use a opção 'checkJS' para obter erros desses arquivos. */
// "checkJs": true, /* Habilite o relatório de erros em arquivos JavaScript com verificação de tipo.*/
// "maxNodeModuleJsDepth": 1, /* Especifique a profundidade máxima da pasta usada para verificar arquivos JavaScript de 'node_modules'. Aplicável somente com 'allowJs'. */
/* Emit */
// "declaration": true, /* Gere arquivos .d.ts a partir de arquivos TypeScript e JavaScript em seu projeto. */
// "declarationMap": true, /* Crie mapas de origem para arquivos d.ts.*/
// "emitDeclarationOnly": true, /*Apenas gere arquivos d.ts e não arquivos JavaScript. */
"sourceMap": true /* Crie arquivos de mapa de origem para arquivos JavaScript emitidos. */,
// "outFile": "./", /*Especifique um arquivo que agrupe todas as saídas em um arquivo JavaScript. Se 'declaração' for verdadeiro, também designa um arquivo que agrupa toda a saída .d.ts. */
"outDir": "./build" /* Especifique uma pasta de saída para todos os arquivos emitidos. */,
// "removeComments": true, /* Desative a emissão de comentários.*/
// "noEmit": true, /* Desative a emissão de arquivos de uma compilação. */
// "importHelpers": true, /* Permita a importação de funções auxiliares de tslib uma vez por projeto, em vez de incluí-las por arquivo. */
// "importsNotUsedAsValues": "remove", /* Especifique o comportamento de emissão/verificação para importações que são usadas apenas para tipos. */
// "downlevelIteration": true, /* Emita JavaScript mais compatível, mas detalhado e com menos desempenho para iteração. */
// "sourceRoot": "", /* Especifique o caminho raiz para que os depuradores localizem o código-fonte de referência. */
// "mapRoot": "", /* Especifique o local onde o depurador deve localizar os arquivos de mapa em vez dos locais gerados. */
// "inlineSourceMap": true, /* Inclua arquivos sourcemap dentro do JavaScript emitido. */
// "inlineSources": true, /* Inclua o código-fonte nos mapas de origem dentro do JavaScript emitido.*/
// "emitBOM": true, /* Emita uma marca de ordem de byte UTF-8 (BOM) no início dos arquivos de saída. */
// "newLine": "crlf", /* Defina o caractere de nova linha para emitir arquivos. */
// "stripInternal": true, /*Desabilite a emissão de declarações que tenham '@internal' em seus comentários JSDoc. */
// "noEmitHelpers": true, /*Desative a geração de funções auxiliares personalizadas como '__extends' na saída compilada.*/
// "noEmitOnError": true, /* Desative a emissão de arquivos se algum erro de verificação de tipo for relatado.*/
// "preserveConstEnums": true, /* Desabilite a remoção das declarações 'const enum' no código gerado. */
// "declarationDir": "./", /* Especifique o diretório de saída para arquivos de declaração gerados.*/
// "preserveValueImports": true, /* Preserve valores importados não utilizados na saída JavaScript que, de outra forma, seriam removidos. */
/* Interop Constraints */
// "isolatedModules": true, /* Certifique-se de que cada arquivo possa ser transpilado com segurança sem depender de outras importações. */
// "allowSyntheticDefaultImports": true, /* Permita 'importar x de y' quando um módulo não tiver uma exportação padrão.*/
"esModuleInterop": true /* Emita JavaScript adicional para facilitar o suporte à importação de módulos CommonJS. Isso habilita 'allowSyntheticDefaultImports' para compatibilidade de tipo.*/,
// "preserveSymlinks": true, /* Desative a resolução de links simbólicos para seu caminho real. Isso se correlaciona com o mesmo sinalizador em node. */
"forceConsistentCasingInFileNames": true /* Assegure-se de que as maiúsculas e minúsculas estejam corretas nas importações.*/,
/* Type Checking */
"strict": true /* Habilite todas as opções rígidas de verificação de tipo. */,
// "noImplicitAny": true, /* Habilite o relatório de erros para expressões e declarações com um tipo 'qualquer' implícito. */
// "strictNullChecks": true, /*Ao verificar o tipo, leve em consideração 'nulo' e 'indefinido'.*/
// "strictFunctionTypes": true, /* Ao atribuir funções, verifique se os parâmetros e os valores de retorno são compatíveis com subtipos. */
// "strictBindCallApply": true, /* Verifique se os argumentos dos métodos 'bind', 'call' e 'apply' correspondem à função original. */
// "strictPropertyInitialization": true, /* Verifique as propriedades de classe que são declaradas, mas não definidas no construtor. */
// "noImplicitThis": true, /* Habilite o relatório de erros quando 'this' receber o tipo 'any'. */
// "useUnknownInCatchVariables": true, /* Variáveis padrão da cláusula catch como 'unknown' em vez de 'any'. */
// "alwaysStrict": true, /* Certifique-se de que 'use strict' seja sempre emitido.*/
// "noUnusedLocals": true, /* Habilite o relatório de erros quando as variáveis locais não forem lidas. */
// "noUnusedParameters": true, /* Gera um erro quando um parâmetro de função não é lido. */
// "exactOptionalPropertyTypes": true, /* Interprete os tipos de propriedade opcionais conforme escritos, em vez de adicionar 'indefinido'. */
// "noImplicitReturns": true, /* Habilite o relatório de erros para codepaths que não retornam explicitamente em uma função. */
// "noFallthroughCasesInSwitch": true, /* Habilite o relatório de erros para casos de fallthrough em instruções switch. */
// "noUncheckedIndexedAccess": true, /*Adicione 'indefinido' a um tipo quando acessado usando um índice. */
// "noImplicitOverride": true, /* Certifique-se de que os membros de substituição em classes derivadas sejam marcados com um modificador de substituição. */
// "noPropertyAccessFromIndexSignature": true, /* Aplique o uso de acessadores indexados para chaves declaradas usando um tipo indexado.*/
// "allowUnusedLabels": true, /* Desative o relatório de erros para rótulos não utilizados. */
// "allowUnreachableCode": true, /* Desative o relatório de erros para código inacessível.*/
/* Completeness */
// "skipDefaultLibCheck": true, /* Ignore os arquivos .d.ts de verificação de tipo que estão incluídos no TypeScript. */
"skipLibCheck": true /* Ignore a verificação de tipo de todos os arquivos .d.ts. */
}
}
Criando Endpoints Iniciais Do Servidor
Agora que temos a configuração inicial do projeto e a configuração dos arquivos, vamos criar dois novos arquivos que hospedam os endpoints e o servidor, respectivamente.
O primeiro arquivo são os endpoints e nossas configurações iniciais do servidor Express com um único endpoints get na raiz para verificar se o servidor está funcionando.
Arquivo: ./src/app.ts
// Imports
// ========================================================
import { config } from "dotenv";
import express from "express";
import cors from "cors";
// ENV VARS
// ========================================================
config();
const NODE_ENV: string = process.env.NODE_ENV || "development";
// Init
// ========================================================
/**
* Initial ExpressJS
*/
const app = express();
// Middlewares
// ========================================================
/**
* Permite solicitações de outros servidores
*/
app.use(cors());
// Endpoints / Routes
// ========================================================
/**
* Endpoint principal para verificar se as coisas estão funcionando e em qual modo de ambiente está sendo executado
*/
app.get("/", (_req, res) => res.send({ environment: NODE_ENV }));
// Exports
// ========================================================
export default app;
Nosso segundo arquivo importa os endpoints e os executa em um servidor específico. Normalmente, você pode ter visto ambos nos mesmos arquivos, mas para testes (abordados em outro artigo), geralmente é mais fácil executar testes de unidade e integração sem que o servidor execute questões separadas.
Arquivo: ./src/server.ts
// Imports
// ========================================================
import app from "./app";
import { config } from "dotenv";
// ENV VARS
// ========================================================
config();
const NODE_ENV: string = process.env.NODE_ENV || "development";
const PORT: number =
NODE_ENV === "production" ? 8080 : parseInt(process.env.PORT || "5001", 10);
// Server
// ========================================================
app.listen(PORT, () =>
console.log(`Listening on PORT ${PORT}\nEnvironment: ${NODE_ENV}`)
);
Em seguida, vamos facilitar para nós mesmos adicionando um comando de script ao nosso arquivo package.json
, para que possamos executar yarn dev
Arquivo: ./package.json
{
"name": "node-filebase-ipfs-uploader",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"dev": "nodemon src/server.ts"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.113.0",
"@types/cors": "^2.8.12",
"@types/express": "^4.17.13",
"@types/multer-s3": "^3.0.0",
"@types/node": "^18.0.0",
"aws-sdk": "^2.1157.0",
"cors": "^2.8.5",
"dotenv": "^16.0.1",
"express": "^4.18.1",
"multer": "^1.4.5-lts.1",
"multer-s3": "^3.0.1",
"typescript": "^4.7.4"
},
"devDependencies": {
"nodemon": "^2.0.16",
"ts-node": "^10.8.1"
}
}
Agora, se executar yarn dev
e abrir nosso navegador em http://localhost:5001
, veremos nosso servidor em execução.
# /node-filebase-ipfs-uploader
yarn dev;
# Expected Output
# $ nodemon src/server.ts
# [nodemon] 2.0.16
# [nodemon] to restart at any time, enter `rs`
# [nodemon] watching path(s): *.*
# [nodemon] watching extensions: ts,json
# [nodemon] starting `ts-node src/server.ts`
# Listening on PORT 5001
# Environment: development
Nosso servidor agora está funcionando, agora precisamos adicionar a funcionalidade de upload.
Adicionando upload de arquivo local para obter uma configuração de código de upload de arquivo, vamos aproveitar um pacote npm chamado multer. O Multer é um middleware que facilita a vida do desenvolvedor na hora de lidar com arquivos. Originalmente, é feito apenas para desenvolvimento local, mas há extensões adicionais que oferecem suporte ao AWS S3, que usaremos para o Filebase.
Vamos modificar nosso arquivo de endpoints original para incluir um novo endpoint de upload POST que utiliza multer.
Arquivo: ./src/app.ts
// Imports
// ========================================================
import { config } from "dotenv";
import express from "express";
import cors from "cors";
import multer from "multer";
// ENV VARS
// ========================================================
config();
const NODE_ENV: string = process.env.NODE_ENV || "development";
const FILE_DEST: string = process.env.FILE_DEST || "bucket";
const FILE_SERVER_URL: string =
process.env.FILE_SERVER_URL || "http://localhost:5002";
// Init
// ========================================================
/**
* Initial ExpressJS
*/
const app = express();
// Middlewares
// ========================================================
/**
* Permite solicitações de outros servidores
*/
app.use(cors());
/**
* Middleware principal do uploader que configura o `destino` final do arquivo e como o `nome do arquivo` seria definido uma vez salvo
*/
const upload = multer({
storage: multer.diskStorage({
destination: (_req, file, callback) => {
callback(null, FILE_DEST);
},
filename: (_req, file, callback) => {
callback(null, file.originalname);
},
}),
});
// Endpoints / Routes
// ========================================================
/**
* Endpoint principal para verificar se as coisas estão funcionando e em qual modo de ambiente está sendo executado
*/
app.get("/", (_req, res) => res.send({ environment: NODE_ENV }));
/**
* Endpoint de upload que aceita um campo de arquivo de entrada de `arquivo`
*/
app.post("/upload", upload.single("file"), (req, res) => {
const responseData = {
file: req.file?.originalname,
url: `${FILE_SERVER_URL}/${req.file?.originalname}`,
};
return res.json({ data: responseData });
});
// Exports
// ========================================================
export default app;
Enquanto o servidor está rodando, vamos fazer um upload.
Curl:
# /node-filebase-ipfs-uploader
curl --location --request POST 'http://localhost:5001/upload' \
--form 'file=@"/full/path/to/node-filebase-ipfs-uploader/test/test-forever.jpg"';
# Expected Output
# {"data":{"file":"test-forever.jpg","url":"http://localhost:5002/test-forever.jpg"}}
Postman:
Para confirmar que o arquivo também foi carregado, podemos verificar nossa pasta bucket para ver um novo test-forever.jpg
criado. Também podemos criar um servidor na porta 5002 para ver que nosso arquivo é carregado executando o seguinte:
# /node-filebase-ipfs-uploader
npx http-server -p 5002 bucket;
# Expected Output
# npx: installed 39 in 3.094s
# Starting up http-server, serving bucket
#
# http-server version: 14.1.1
#
# http-server settings:
# CORS: disabled
# Cache: 3600 seconds
# Connection Timeout: 120 seconds
# Directory Listings: visible
# AutoIndex: visible
# Serve GZIP Files: false
# Serve Brotli Files: false
# Default File Extension: none
#
# Available on:
# http://127.0.0.1:5002
# http://10.0.0.6:5002
# Hit CTRL-C to stop the server
Se abrirmos http://localhost:5002/test-forever.jpg
podemos ver em nosso navegador que a imagem está lá.
Agora que temos a configuração básica para nossos uploads locais, vamos aproveitar o multer-s3 para utilizar a compatibilidade do cliente AWS S3 do Filebase.
Adicionando Suporte Filebase IPFS
Antes de podermos adicionar o código, precisamos criar uma nova conta no Filebase.com. Depois que uma conta for criada, precisaremos criar um novo bucket, com a rede de armazenamento definida como IPFS (todos os dados são públicos).
Assim que tivermos nosso bucket recém-criado, vamos anotar o nome para que possamos usá-lo mais tarde e, em seguida, obter nossas chaves de acesso.
Agora que temos os valores, vamos criar um arquivo de ambiente dot .env
, mas vamos criar um modelo (.env.example) para ele porque nunca devemos salvar nosso .env
em nosso repositório git.
# /node-filebase-ipfs-uploader
echo "PORT=5001\nNODE_ENV=development\nFILEBASE_ACCESS_KEY=key\nFILEBASE_SECRET_KEY=secret\nFILEBASE_BUCKET=bucket\nFILEBASE_REGION=us-east-1\nFILE_SERVER_URL=http://localhost:5002" > .env.example;
cp .env.example .env;
Em nosso .env
copiado, precisaremos preenchê-lo com os valores que acabamos de obter do Filebase.
Arquivo: ./.env
PORT=5001
NODE_ENV=development
FILEBASE_ACCESS_KEY=<YOUR-FILEBASE-ACCESS-KEY>
FILEBASE_SECRET_KEY=<YOUR-FILEBASE-SECRET-KEY>
FILEBASE_BUCKET=<YOUR-FILEBASE-BUCKET-NAME>
FILEBASE_REGION=us-east-1
FILE_SERVER_URL=http://localhost:5002
O que queremos fazer é criar uma maneira para que, quando estivermos executando no modo de desenvolvimento
, os arquivos sejam carregados em nossa pasta bucket
local, mas quando estivermos no modo de produção, os arquivos sejam carregados no IPFS com o Filebase. Para fazer isso, vamos aproveitar nosso NODE_ENV
e modificar nosso middleware de upload
para usar uma configuração multer diferente quando o NODE_ENV
estiver definido para produção. Mais especificamente, usaremos uma extensão do multer
chamada multer-s3
que lida com solicitações regularmente para o AWS S3, mas como o Filebase é um serviço compatível com o AWS S3, vamos apenas configurá-lo para apontar para o Filebase.
Arquivo: ./src/app.ts
// Imports
// ========================================================
import { config } from "dotenv";
import express from "express";
import cors from "cors";
import multer from "multer";
import multerS3 from "multer-s3";
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
// ENV VARS
// ========================================================
config();
const NODE_ENV: string = process.env.NODE_ENV || "development";
const FILE_DEST: string = process.env.FILE_DEST || "bucket";
const FILE_SERVER_URL: string =
process.env.FILE_SERVER_URL || "http://localhost:5002";
const FILEBASE_BUCKET = process.env.FILEBASE_BUCKET || "";
// Configured AWS S3 Client For Filebase
const s3 = new S3Client({
endpoint: "https://s3.filebase.com",
region: process.env.FILEBASE_REGION || "",
credentials: {
accessKeyId: process.env.FILEBASE_ACCESS_KEY || "",
secretAccessKey: process.env.FILEBASE_SECRET_KEY || "",
},
});
// Init
// ========================================================
/**
* Initial ExpressJS
*/
const app = express();
// Middlewares
// ========================================================
/**
* Permite solicitações de outros servidores
*/
app.use(cors());
/**
* Middleware principal do uploader que configura o `destino` final do arquivo e como o `nome do arquivo` seria definido uma vez salvo
*/
const upload =
// If production use the s3 client
NODE_ENV === "production"
? multer({
storage: multerS3({
s3: s3,
bucket: FILEBASE_BUCKET,
metadata: (_req, file, cb) => {
cb(null, { fieldName: file.fieldname });
},
key: (_req, file, cb) => {
cb(null, file.originalname);
},
}),
})
: multer({
storage: multer.diskStorage({
destination: (_req, file, callback) => {
callback(null, FILE_DEST);
},
filename: (_req, file, callback) => {
callback(null, file.originalname);
},
}),
});
// Endpoints / Routes
// ========================================================
/**
* Endpoint principal para verificar se as coisas estão funcionando e em qual modo de ambiente está sendo executado
*/
app.get("/", (_req, res) => res.send({ environment: NODE_ENV }));
/**
* Endpoint de upload que aceita um campo de arquivo de entrada de `arquivo`
*/
app.post("/upload", upload.single("file"), async (req, res) => {
const responseData = {
file: req.file?.originalname,
url: `${FILE_SERVER_URL}/${req.file?.originalname}`,
};
// Se a produção recuperar os dados do arquivo para obter o CID do ipfs
if (NODE_ENV === "production") {
const commandGetObject = new GetObjectCommand({
Bucket: FILEBASE_BUCKET,
Key: req.file?.originalname,
});
const response = await s3.send(commandGetObject);
responseData.url = `ipfs://${response.Metadata?.cid}`;
}
return res.json({ data: responseData });
});
// Exports
// ========================================================
export default app;
Agora, para testá-lo, precisamos apenas criar um script de inicialização diferente que passe um novo NODE_ENV
. Para fazer isso, precisaremos modificar nosso package.json
Arquivo: ./package.json
{
"name": "node-filebase-ipfs-uploader",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"dev": "nodemon src/server.ts",
"start": "export NODE_ENV=production && tsc && node build/server.js"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.113.0",
"@types/cors": "^2.8.12",
"@types/express": "^4.17.13",
"@types/multer-s3": "^3.0.0",
"@types/node": "^18.0.0",
"aws-sdk": "^2.1157.0",
"cors": "^2.8.5",
"dotenv": "^16.0.1",
"express": "^4.18.1",
"multer": "^1.4.5-lts.1",
"multer-s3": "^3.0.1",
"typescript": "^4.7.4"
},
"devDependencies": {
"nodemon": "^2.0.16",
"ts-node": "^10.8.1"
}
}
Agora, quando executamos o seguinte, devemos ver nosso servidor rodando na porta 8080.
# /node-filebase-ipfs-uploader
yarn start;
# Expected Output
# yarn run v1.22.18
# $ export NODE_ENV=production && tsc && node build/server.js
# Listening on PORT 8080
# Environment: production
Se tentarmos carregar nosso arquivo novamente com curl ou Postman com o novo endereço, devemos obter um resultado diferente.
Curl:
# /node-filebase-ipfs-uploader
curl --location --request POST 'http://localhost:8080/upload' \
--form 'file=@"/full/path/to/node-filebase-ipfs-uploader/test/test-forever.jpg"';
# Saída Esperada
# {"data":{"file":"test-forever.jpg","url":"ipfs://QmY8UXb7Nka5VWkXXRAMC1DQtamc1s8xzcjoQY6GaEbTdn"}}
Postman:
Ao ver o URL do IPFS, podemos acessá-lo diretamente e ver se ele realmente persiste no armazenamento descentralizado.
https://ipfs.filebase.io/ipfs/bafybeierozrtdlir5ywdguah573ga25a7tzzhqeh7kvlvfx2y2wqymdj7u
Aí está, construímos um Uploader de arquivos IPFS de back-end com Filebase.
Qual é o próximo?
Agora que você tem o back-end, a próxima etapa é criar um front-end que se comunique com o back-end.
Outro aspecto que poderia ser trabalhado é a implantação do back-end para um serviço como Digital Ocean com Docker ou Netlify com Edge Functions.
Esse artigo é uma tradução feita por @bananlabs. Você pode encontrar o artigo original aqui
Top comments (0)