Add a New Qwik Project
The code for this example is available on GitHub:
Example repository/nrwl/nx-recipes/tree/main/qwik
Supported Features
We'll be using an Nx Plugin for Qwik called qwik-nx.
✅ Run Tasks ✅ Cache Task Results ✅ Share Your Cache ✅ Explore the Graph ✅ Distribute Task Execution ✅ Integrate with Editors ✅ Automate Updating Nx ✅ Enforce Module Boundaries ✅ Use Task Executors ✅ Use Code Generators ✅ Automate Updating Framework Dependencies
Install the qwik-nx Plugin
Install the qwik-nx plugin:
❯
npm i --save-dev qwik-nx
You can find a compatibility matrix for qwik-nx here: https://github.com/qwikifiers/qwik-nx#qwik-nx--nx-compatibility-chart.
You can use this to help you understand which version of qwik-nx you should install based on the version of nx you are currently using.
If you need help finding the version of nx you are currently using, run nx report.
Create the application
Let's generate a new application using qwik-nx.
❯
nx g qwik-nx:app todo
Create a library
Let's generate a new library using qwik-nx.
❯
nx g qwik-nx:lib data-access
Create a Context in the library
We'll add a Context to the library to store some state.
Create a new file libs/data-access/src/lib/todo.context.tsx with the following content:
import {
component$,
createContextId,
Slot,
useContextProvider,
useStore,
} from '@builder.io/qwik';
export interface Todo {
id: number;
message: string;
}
interface TodoStore {
todos: Todo[];
lastId: number;
}
export const TodoContext = createContextId<TodoStore>('todo.context');
export const TodoContextProvider = component$(() => {
const todoStore = useStore<TodoStore>({
todos: [],
lastId: 0,
});
useContextProvider(TodoContext, todoStore);
return <Slot />;
});
We'll use this context to store the state for our application.
Let's create a new file to handle some of the logic for our application.
Create libs/data-access/src/lib/todo.ts and add the following:
import { Todo } from './todo.context';
// A rudimentary in-mem DB that will run on the server
interface DB {
store: Record<string, any[]>;
get: (storeName: string) => any[];
set: (storeName: string, value: any[]) => boolean;
add: (storeName: string, value: any) => boolean;
}
export const db: DB = {
store: { todos: [] },
get(storeName) {
return db.store[storeName];
},
set(storeName, value) {
try {
db.store[storeName] = value;
return true;
} catch (e) {
return false;
}
},
add(storeName, value) {
try {
db.store[storeName].push(value);
return true;
} catch (e) {
return false;
}
},
};
export function getTodos() {
// A network request or db connection could be made here to fetch persisted todos
// For illustrative purposes, we're going to seed a rudimentary in-memory DB if it hasn't been already
// Then return the value from it
if (db.get('todos')?.length === 0) {
db.set('todos', [
{
id: 1,
message: 'First todo',
},
]);
}
const todos: Todo[] = db.get('todos');
const lastId = [...todos].sort((a, b) => b.id - a.id)[0].id;
return { todos, lastId };
}
export function addTodo(todo: { id: string; message: string }) {
const success = db.add('todos', {
id: parseInt(todo.id),
message: todo.message,
});
return { success };
}
Update libs/data-access/src/index.ts to export our new context and methods:
export * from './lib/todo.context';
export * from './lib/todo';
Generate a Route
Next, let's generate a route to store the logic for the application.
❯
nx g qwik-nx:route --name=todo --project=todo
We will use our new context Update the new route file (apps/todo/src/routes/todo/index.tsx) to the following:
import { component$, useContext, useTask$ } from '@builder.io/qwik';
import {
Form,
routeAction$,
routeLoader$,
z,
zod$,
} from '@builder.io/qwik-city';
import { addTodo, getTodos, TodoContext } from '@acme/data-access';
export const useGetTodos = routeLoader$(() => getTodos());
export const useAddTodo = routeAction$(
(todo) => addTodo(todo),
zod$({ id: z.string(), message: z.string() })
);
export default component$(() => {
const todoStore = useContext(TodoContext);
const persistedTodos = useGetTodos();
const addTodoAction = useAddTodo();
useTask$(({ track }) => {
track(() => persistedTodos.value);
if (persistedTodos.value) {
todoStore.todos = persistedTodos.value.todos;
todoStore.lastId =
todoStore.lastId > persistedTodos.value.lastId
? todoStore.lastId
: persistedTodos.value.lastId;
}
});
return (
<div>
<h1>Todos</h1>
{todoStore.todos.map((t) => (
<div key={`todo-${t.id}`}>
<label>
<input type="checkbox" /> {t.message}
</label>
</div>
))}
<Form action={addTodoAction}>
<input type="hidden" name="id" value={todoStore.lastId + 1} />
<input type="text" name="message" />
<button type="submit">Add</button>
</Form>
{addTodoAction.value?.success && <p>Todo added!</p>}
</div>
);
});
Build and Serve the Application
To serve the application, run the following command and then navigate your browser to http://localhost:4200/todo
❯
nx serve todo
To build the application, run the following command:
❯
nx build todo