How to Upload Files with Soki in Next.js
VienDinhCom opened this issue · 0 comments
Uploading files is one of the most important tasks while we're working on a web project. So, in the article, I will show you how I upload files with soki.
Schemas
File Schema
In this section, I will create a function type called upload. And I will put this function type into a child schema called FileSchema. The hello function will have the input and the output like the code below.
// src/shared/schemas/file.schema.ts
import { createSchema, fn, z } from 'soki';
export const FileSchema = createSchema({
upload: fn({
input: {
file: z.file(),
},
output: z.string(), // file name
}),
});
Root Schema
Next, I will put the FileSchema into the RootSchema like this.
// src/shared/schemas/root.schema.ts
import { createRootSchema } from 'soki';
import { FileSchema } from './file.schema';
import { MessageSchema } from './message.schema';
export const RootSchema = createRootSchema({
file: FileSchema,
message: MessageSchema,
});
On Server
File Resolver
This step, I will implement FileSchema with the Resolvers['file'] type. The upload function will return the name of the file I'm going to upload.
// src/backend/resolvers/file.resolver.ts
import { Resolvers, createResolver } from '@backend/core';
export const FileResolver = createResolver<Resolvers['file']>({
upload: async ({ file }, context) => {
return file.name;
},
});
Root Resolver
Next, I will put the FileResolver into the RootResolver like this.
// src/backend/resolvers/root.resolver.ts
import { Context, createRootResolver } from '@backend/core';
import { RootSchema } from '@shared/schemas/root.schema';
import { FileResolver } from './file.resolver';
import { MessageResolver } from './message.resolver';
export const RootResolver = createRootResolver({
RootSchema,
resolvers: {
file: FileResolver,
message: MessageResolver,
},
context: async (req, res): Promise<Context> => {
return {};
},
});
On Client
Finally, I will create a form and an upload function. So I can call the upload function from the FileResolver.
// src/pages/upload.tsx
import { useState } from 'react';
import { DefaultLayout } from '@frontend/components/Layouts/DefaultLayout';
import { ApiService, useMuation, getFiles, File } from '@frontend/services/api.service';
export default function Page() {
const [file, setFile] = useState<File>();
const { loading, data, error, mutate } = useMuation(async (file: File) => {
return ApiService.file.upload({ file });
});
return (
<DefaultLayout>
<h1>Upload Files</h1>
<form
onSubmit={(event) => {
event.preventDefault();
if (file) mutate(file);
}}
>
<input
type="file"
onChange={(event) => {
const files = getFiles(event.currentTarget.files);
setFile(files[0]);
}}
/>
<br />
{error && <p>Error: {error.message}</p>}
{data && (
<p>
<b>{data}</b> is uploaded!
</p>
)}
<p>
<button type="submit">{loading ? 'Uploading...' : 'Upload Now!'}</button>
</p>
</form>
</DefaultLayout>
);
}
To test the result, please run yarn dev
and go to http://localhost:3000/upload to see how it works.
Demo: https://next-full-stack-git-issue-5.maxvien.vercel.app/upload
Source Code
You can find the source code of this tutorial in this branch: https://github.com/Maxvien/next-full-stack/tree/issue-5