Skip to content

Commit 94d3ed0

Browse files
updated vue tutorial (#1900)
Co-authored-by: Phil Hawksworth <phil@deno.com>
1 parent 866b58d commit 94d3ed0

File tree

2 files changed

+199
-75
lines changed

2 files changed

+199
-75
lines changed

examples/tutorials/react.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -430,9 +430,9 @@ You can see a version of the
430430

431431
## Build and deploy
432432

433-
The project includes a `serve` task that builds the React app and serves it with
434-
the Oak backend server. Run the following command to build and serve the app in
435-
production mode:
433+
We set up the project with a `serve` task that builds the React app and serves
434+
it with the Oak backend server. Run the following command to build and serve the
435+
app in production mode:
436436

437437
```sh
438438
deno run build
@@ -446,7 +446,7 @@ This will:
446446

447447
Visit `localhost:8000` in your browser to see the production version of the app!
448448

449-
You can deploy this app to your favouite cloud provider. We recommend using
449+
You can deploy this app to your favorite cloud provider. We recommend using
450450
[Deno Deploy](https://deno.com/deploy) for a simple and easy deployment
451451
experience. You can deploy your app directly from GitHub, simply create a GitHub
452452
repository and push your code there, then connect it to Deno Deploy.
@@ -460,7 +460,7 @@ push your app to GitHub:
460460
git init -b main
461461
git remote add origin https://github.com/<your_github_username>/<your_repo_name>.git
462462
git add .
463-
git commit -am 'my next app'
463+
git commit -am 'my react app'
464464
git push -u origin main
465465
```
466466

examples/tutorials/vue.md

Lines changed: 194 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ will display a list of dinosaurs. When you click on one, it'll take you to a
1616
dinosaur page with more details. You can see the
1717
[finished app on GitHub](https://github.com/denoland/tutorial-with-vue).
1818

19-
![The Vue.js app in action](./images/how-to/vue/vue.gif)
19+
You can see a live version of the app on
20+
[Deno Deploy](https://tutorial-with-vue.deno.deno.net/).
2021

2122
## Create a Vue.js app with Vite and Deno
2223

@@ -63,101 +64,162 @@ level of your `deno.json` file:
6364
"unstable": ["fmt-component"]
6465
```
6566

66-
## Add a backend
67+
## Add a backend API
6768

68-
The next step is to add a backend API. We'll create a very simple API that
69-
returns information about dinosaurs.
69+
We'll build an API server using Deno and Oak. This will be where we get our
70+
dinosaur data.
7071

71-
In the root of your new vite project, create an `api` folder. In that folder,
72-
create a `main.ts` file, which will run the server, and a `data.json`, which
73-
where we'll put the hard coded data.
72+
In the root directory of your project, create an `api` folder. In that folder,
73+
create a `data.json`, which will contain the hard coded dinosaur data.
7474

7575
Copy and paste
76-
[this json file](https://raw.githubusercontent.com/denoland/tutorial-with-vue/refs/heads/main/api/data.json)
77-
into `api/data.json`.
76+
[this json file](https://github.com/denoland/tutorial-with-react/blob/main/api/data.json)
77+
into the `api/data.json` file. (If you were building a real app, you would
78+
probably fetch this data from a database or an external API.)
7879

79-
We're going to build out a simple API server with routes that return dinosaur
80-
information. We'll use the [`oak` middleware framework](https://jsr.io/@oak/oak)
81-
and the [`cors` middleware](https://jsr.io/@tajpouria/cors) to enable
80+
We're going to build out some API routes that return dinosaur information. We'll
81+
need Oak for the HTTP server and
82+
[CORS middleware](https://jsr.io/@tajpouria/cors) to enable
8283
[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS).
8384

84-
Use the `deno add` command to add the required dependencies to your project:
85+
Add the dependencies to your `deno.json` file by updating the imports section:
8586

86-
```shell
87-
deno add jsr:@oak/oak jsr:@tajpouria/cors
87+
```json title="deno.json"
88+
{
89+
"imports": {
90+
"@oak/oak": "jsr:@oak/oak@^17.1.5",
91+
"@tajpouria/cors": "jsr:@tajpouria/cors@^1.2.1",
92+
"vue-router": "npm:vue-router@^4.5.1"
93+
}
94+
}
8895
```
8996

90-
Next, update `api/main.ts` to import the required modules and create a new
97+
Next, create `api/main.ts` and import the required modules and create a new
9198
`Router` instance to define some routes:
9299

93-
```ts title="main.ts"
100+
```ts title="api/main.ts"
94101
import { Application, Router } from "@oak/oak";
95102
import { oakCors } from "@tajpouria/cors";
103+
import routeStaticFilesFrom from "./util/routeStaticFilesFrom.ts";
96104
import data from "./data.json" with { type: "json" };
97105

106+
export const app = new Application();
98107
const router = new Router();
99108
```
100109

101-
After this, in the same file, we'll define three routes. The first route at `/`
102-
will return the string `Welcome to the dinosaur API`, then we'll set up
103-
`/dinosaurs` to return all the dinosaurs, and finally `/dinosaurs/:dinosaur` to
104-
return a specific dinosaur based on the name in the URL:
105-
106-
```ts title="main.ts"
107-
router
108-
.get("/", (context) => {
109-
context.response.body = "Welcome to dinosaur API!";
110-
})
111-
.get("/dinosaurs", (context) => {
112-
context.response.body = data;
113-
})
114-
.get("/dinosaurs/:dinosaur", (context) => {
115-
if (!context?.params?.dinosaur) {
116-
context.response.body = "No dinosaur name provided.";
117-
}
110+
After this, in the same file, we'll define two routes. One at `/api/dinosaurs`
111+
to return all the dinosaurs, and `/api/dinosaurs/:dinosaur` to return a specific
112+
dinosaur based on the name in the URL:
118113

119-
const dinosaur = data.find((item) =>
120-
item.name.toLowerCase() === context.params.dinosaur.toLowerCase()
121-
);
114+
```ts title="api/main.ts"
115+
router.get("/api/dinosaurs", (context) => {
116+
context.response.body = data;
117+
});
122118

123-
context.response.body = dinosaur ? dinosaur : "No dinosaur found.";
124-
});
119+
router.get("/api/dinosaurs/:dinosaur", (context) => {
120+
if (!context?.params?.dinosaur) {
121+
context.response.body = "No dinosaur name provided.";
122+
}
123+
124+
const dinosaur = data.find((item) =>
125+
item.name.toLowerCase() === context.params.dinosaur.toLowerCase()
126+
);
127+
128+
context.response.body = dinosaur ?? "No dinosaur found.";
129+
});
125130
```
126131

127-
Finally, at the bottom of the same file, create a new `Application` instance and
128-
attach the routes we just defined to the application using
129-
`app.use(router.routes())` and start the server listening on port 8000:
132+
At the bottom of the same file, attach the routes we just defined to the
133+
application. We also must include the static file server, and finally we'll
134+
start the server listening on port 8000:
130135

131-
```ts title="main.ts"
132-
const app = new Application();
136+
```ts title="api/main.ts"
133137
app.use(oakCors());
134138
app.use(router.routes());
135139
app.use(router.allowedMethods());
140+
app.use(routeStaticFilesFrom([
141+
`${Deno.cwd()}/dist`,
142+
`${Deno.cwd()}/public`,
143+
]));
144+
145+
if (import.meta.main) {
146+
console.log("Server listening on port http://localhost:8000");
147+
await app.listen({ port: 8000 });
148+
}
149+
```
136150

137-
await app.listen({ port: 8000 });
151+
You'll also need to create the `api/util/routeStaticFilesFrom.ts` file to serve
152+
static files:
153+
154+
```ts title="api/util/routeStaticFilesFrom.ts"
155+
import { Context, Next } from "@oak/oak";
156+
157+
// Configure static site routes so that we can serve
158+
// the Vite build output and the public folder
159+
export default function routeStaticFilesFrom(staticPaths: string[]) {
160+
return async (context: Context<Record<string, object>>, next: Next) => {
161+
for (const path of staticPaths) {
162+
try {
163+
await context.send({ root: path, index: "index.html" });
164+
return;
165+
} catch {
166+
continue;
167+
}
168+
}
169+
170+
await next();
171+
};
172+
}
138173
```
139174

140-
You can run the API server with `deno run --allow-env --allow-net api/main.ts`.
141-
We'll create a task to run this command and update the dev task to run both the
142-
Vue.js app and the API server.
175+
You can run the API server with
176+
`deno run --allow-env --allow-net --allow-read api/main.ts`. We'll create a task
177+
to run this command in the background and update the dev task to run both the
178+
Vue app and the API server.
143179

144-
In your `package.json` file, update the `scripts` field to include the
145-
following:
180+
Update your `package.json` scripts to include the following:
146181

147-
```jsonc
182+
```json title="package.json"
148183
{
149184
"scripts": {
150185
"dev": "deno task dev:api & deno task dev:vite",
151186
"dev:api": "deno run --allow-env --allow-net api/main.ts",
152187
"dev:vite": "deno run -A npm:vite",
153-
// ...
188+
"build": "deno run -A npm:vite build",
189+
"server:start": "deno run -A --watch ./api/main.ts",
190+
"serve": "deno run build && deno run server:start",
191+
"preview": "vite preview"
192+
}
154193
}
155194
```
156195

157-
Now, if you run `deno task dev` and visit `localhost:8000`, in your browser you
158-
should see the text `Welcome to dinosaur API!`, and if you visit
159-
`localhost:8000/dinosaurs`, you should see a JSON response of all of the
160-
dinosaurs.
196+
Make sure your `vite.config.ts` includes the Deno plugin and proxy configuration
197+
for development:
198+
199+
```ts title="vite.config.ts"
200+
import { defineConfig } from "vite";
201+
import vue from "@vitejs/plugin-vue";
202+
import deno from "@deno/vite-plugin";
203+
204+
export default defineConfig({
205+
server: {
206+
port: 3000,
207+
proxy: {
208+
"/api": {
209+
target: "http://localhost:8000",
210+
changeOrigin: true,
211+
},
212+
},
213+
},
214+
plugins: [vue(), deno()],
215+
optimizeDeps: {
216+
include: ["react/jsx-runtime"],
217+
},
218+
});
219+
```
220+
221+
If you run `npm run dev` now and visit `localhost:8000/api/dinosaurs`, in your
222+
browser you should see a JSON response of all of the dinosaurs.
161223

162224
## Build the frontend
163225

@@ -182,10 +244,16 @@ createApp(App)
182244
.mount("#app");
183245
```
184246

185-
Add the Vue Router module to the project with `deno add`:
247+
Add the Vue Router module to the project by updating your `deno.json` imports:
186248

187-
```shell
188-
deno add npm:vue-router
249+
```json title="deno.json"
250+
{
251+
"imports": {
252+
"@oak/oak": "jsr:@oak/oak@^17.1.5",
253+
"@tajpouria/cors": "jsr:@tajpouria/cors@^1.2.1",
254+
"vue-router": "npm:vue-router@^4.5.1"
255+
}
256+
}
189257
```
190258

191259
Next, create a `router` directory in the `src` directory. In it, create an
@@ -255,21 +323,21 @@ up earlier and render them as links using the
255323
256324
export default defineComponent({
257325
async setup() {
258-
const res = await fetch("http://localhost:8000/dinosaurs");
326+
const res = await fetch("/api/dinosaurs");
259327
const dinosaurs = await res.json() as Dinosaur[];
260328
return { dinosaurs };
261329
},
262330
});
263331
</script>
264332

265333
<template>
266-
<div v-for="dinosaur in dinosaurs" :key="dinosaur.name">
334+
<span v-for="dinosaur in dinosaurs" :key="dinosaur.name">
267335
<RouterLink
268336
:to="{ name: 'Dinosaur', params: { dinosaur: `${dinosaur.name.toLowerCase()}` } }"
269337
>
270338
{{ dinosaur.name }}
271339
</RouterLink>
272-
</div>
340+
</span>
273341
</template>
274342
```
275343

@@ -341,7 +409,7 @@ Then update the `Dinosaur.vue` file:
341409
},
342410
async mounted() {
343411
const res = await fetch(
344-
`http://localhost:8000/dinosaurs/${this.dinosaur}`,
412+
`/api/dinosaurs/${this.dinosaur}`,
345413
);
346414
this.dinosaurDetails = await res.json();
347415
},
@@ -366,16 +434,72 @@ Now that we've set up the frontend and backend, we can run the app. In your
366434
terminal, run the following command:
367435

368436
```shell
369-
deno task dev
437+
npm run dev
370438
```
371439

372-
Visit the output localhost link in your browser to see the app. Click on a
440+
This will start both the Deno API server on port 8000 and the Vite development
441+
server on port 3000. The Vite server will proxy API requests to the Deno server.
442+
443+
Visit `http://localhost:3000` in your browser to see the app. Click on a
373444
dinosaur to see more details!
374445

375-
![The vue app in action](./images/how-to/vue/vue.gif)
446+
You can see a live version of the app on
447+
[Deno Deploy](https://tutorial-with-vue.deno.deno.net/).
448+
449+
[The vue app in action](./images/how-to/vue/vue.gif)
450+
451+
```shell
452+
deno run serve
453+
```
454+
455+
This will build the Vue app and serve everything from the Deno server on
456+
port 8000.
457+
458+
## Build and deploy
459+
460+
We set up the project with a `serve` task that builds the React app and serves
461+
it with the Oak backend server. Run the following command to build and serve the
462+
app in production mode:
463+
464+
```sh
465+
deno run build
466+
deno run serve
467+
```
468+
469+
This will:
470+
471+
1. Build the Vue app using Vite (output goes to `dist/`)
472+
2. Start the Oak server which serves both the API and the built Vue app
473+
474+
Visit `localhost:8000` in your browser to see the production version of the app!
475+
476+
You can deploy this app to your favorite cloud provider. We recommend using
477+
[Deno Deploy](https://deno.com/deploy) for a simple and easy deployment
478+
experience. You can deploy your app directly from GitHub, simply create a GitHub
479+
repository and push your code there, then connect it to Deno Deploy.
480+
481+
### Create a GitHub repository
482+
483+
[Create a new GitHub repository](https://github.com/new), then initialize and
484+
push your app to GitHub:
485+
486+
```sh
487+
git init -b main
488+
git remote add origin https://github.com/<your_github_username>/<your_repo_name>.git
489+
git add .
490+
git commit -am 'my vue app'
491+
git push -u origin main
492+
```
493+
494+
### Deploy to Deno Deploy
495+
496+
Once your app is on GitHub, you can deploy it on the Deno Deploy<sup>EA</sup>
497+
dashboard.
498+
<a href="https://app.deno.com/" class="docs-cta deploy-cta deploy-button">Deploy
499+
my app</a>
500+
501+
For a walkthrough of deploying your app, check out the
502+
[Deno Deploy tutorial](/examples/deno_deploy_tutorial/).
376503

377504
🦕 Now that you can run a Vue app in Deno with Vite you're ready to build real
378-
world applications! If you'd like to expand upon this demo you could consider
379-
building out a backend server to serve the static app once built, then you'll be
380-
able to
381-
[deploy your dinosaur app to the cloud](https://docs.deno.com/deploy/manual/).
505+
world applications!

0 commit comments

Comments
 (0)