Lesson 2: Module Creation
Our magic add-module
tool will handle all the configurations and create files for all the layers (presentation, service, data) with basic implementations. You'll just need to modify them according to your needs. Since each layer has a single responsibility, you can leverage LLMs to help generate the content for each layer file after our tool gernates automatcially those files and configs - they excel at handling focused, single-purpose tasks like this.
Create the Module
npx create-jaseci-app add-module post \
--node=Post \
--path=dashboard/posts \
--node-type="id:number,title:string,completed:boolean,userId:number" \
--apis="list,get,create,update,delete" \
--auth=no \
--api-base=/todos
Let's understand each parameter:
-
post
(module_name):- The base name for our module
- Creates the module directory at
modules/posts/
- Used as the default route path unless overridden
-
--node=Post
:- Custom node name for the module
- Generates
PostNode
interface instead ofPostNode
- Used in TypeScript types and Redux state
-
--path=dashboard/posts
:- Custom route path for the module
- Page will be accessible at
/dashboard/posts
- Supports nested routes and route groups
-
--node-type="id:number,title:string,completed:boolean,userId:number"
:- Defines the Post node structure
- Each field is defined as
name:type
- Matches JSONPlaceholder's todo structure
- Supports basic types (string, number, boolean)
-
--apis="list,get,create,update,delete"
:- Standard CRUD operations to generate
- Each endpoint will be implemented in the service layer
- Used to interact with the API
-
--auth=no
:- Uses
public_api
instead ofprivate_api
- Makes the page publicly accessible
- No authentication required
- Suitable for public APIs like JSONPlaceholder
- Uses
-
--api-base=/todos
:- Base path for API endpoints
- All API calls will be prefixed with
/todos
- Matches JSONPlaceholder's API structure
This command will:
- Create a tasks module
- Set up a Post node matching JSONPlaceholder's todo structure
- Create a route at
/dashboard/posts
- Configure CRUD API endpoints using public API (since
--auth=no
) - Set up API base path to
/todos
Generated Structure
modules/posts/
├── actions/ # Redux actions
├── hooks/ # Custom hooks
├── pages/ # Page components
├── schemas/ # Validation schemas
├── services/ # API services
└── utils/ # Utility functions
Node Definition
The Post node is automatically created to match JSONPlaceholder's todo structure:
// nodes/post-node.ts
export interface PostNode {
id: number;
title: string;
completed: boolean;
userId: number;
}
API Service
The API service is configured to use the public_api client (since --auth=no
) for JSONPlaceholder endpoints:
// modules/posts/services/post-service.ts
import { PostNode } from '@/nodes/post-node';
import { public_api } from '@/_core/api-client';
export const PostApi = {
// Get all posts
getPosts: async () => {
const response = await public_api.get('/todos');
return response.data;
},
// Get a specific post
getPost: async (id: number) => {
const response = await public_api.get(`/todos/${id}`);
return response.data;
},
// Create a new post
createPost: async (data: Omit<PostNode, 'id'>) => {
const response = await public_api.post('/todos', data);
return response.data;
},
// Update an existing post
updatePost: async (id: number, data: Partial<PostNode>) => {
const response = await public_api.put(`/todos/${id}`, data);
return response.data;
},
// Delete a post
deletePost: async (id: number) => {
const response = await public_api.delete(`/todos/${id}`);
return response.data;
}
};
Redux Slice
The Redux slice is automatically created:
// store/postslice.ts
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
import { PostNode } from '@/nodes/post-node';
import { fetchPosts } from '../modules/post/actions';
interface PostState {
items: PostNode[];
isLoading: boolean;
error: string | null;
success: boolean;
successMessage: string | null;
}
const initialState: PostState = {
items: [],
isLoading: false,
error: null,
success: false,
successMessage: null,
};
export const postSlice = createSlice({
name: 'post',
initialState,
reducers: {
setItems: (state, action: PayloadAction<PostNode[]>) => {
state.items = action.payload;
},
setLoading: (state, action: PayloadAction<boolean>) => {
state.isLoading = action.payload;
},
setError: (state, action: PayloadAction<string | null>) => {
state.error = action.payload;
state.success = false;
},
resetSuccess: (state) => {
state.success = false;
state.successMessage = null;
},
},
extraReducers: (builder) => {
builder.addCase(fetchPosts.pending, (state) => {
state.isLoading = true;
state.error = null;
state.success = false;
state.successMessage = null;
});
builder.addCase(fetchPosts.fulfilled, (state, action) => {
state.isLoading = false;
state.items = action.payload;
state.success = true;
state.successMessage = 'Posts fetched successfully';
});
builder.addCase(fetchPosts.rejected, (state, action) => {
state.isLoading = false;
state.error = action.payload as string;
state.success = false;
});
},
});
export const { setItems, setLoading, setError, resetSuccess } = postSlice.actions;
export default postSlice.reducer;
Verify Module Creation
- Check that all files are created
- Verify the route is accessible at
http://localhost:3001/dashboard/posts
Next Steps
In the next step, we'll:
- Customize the Post node
- Add validation schemas
- Implement custom hooks