Close Menu
Soshace Digital Blog

    Subscribe to Updates

    Get The Latest News, Updates, And Amazing Offers

    What's Hot
    Beginners

    What Is Responsive Web Design?

    Trends

    7 Job Recruitment Trends 2019 | From Self-Service Modules to Peculiarities of Gen Z

    Programming

    6. Уроки Node.js. Util и Наследование

    Important Pages:
    • Home
    • About
    • Services
    • Contact Us
    • Privacy Policy
    • Terms & Conditions
    Facebook X (Twitter) Instagram LinkedIn YouTube
    Today's Picks:
    • Scaling Success: Monitoring Indexation of Programmatic SEO Content
    • Leveraging Influencers: Key Drivers in New Product Launches
    • How Privacy-First Marketing Will Transform the Industry Landscape
    • The Impact of Social Proof on Thought Leadership Marketing
    • Balancing Value-Driven Content and Promotional Messaging Strategies
    • Top Influencer Marketing Platforms to Explore in 2025
    • Emerging Trends in Marketing Automation and AI Tools for 2023
    • Strategies to Mitigate Duplicate Content in Programmatic SEO
    Wednesday, August 27
    Facebook X (Twitter) Instagram LinkedIn YouTube
    Soshace Digital Blog
    • Home
    • About
    • Services
    • Contact Us
    • Privacy Policy
    • Terms & Conditions
    Services
    • SaaS & Tech

      Maximizing Efficiency: How SaaS Lowers IT Infrastructure Costs

      August 27, 2025

      Navigating Tomorrow: Innovations Shaping the Future of SaaS

      August 27, 2025

      Maximizing Impact: Strategies for SaaS & Technology Marketing

      August 27, 2025
    • AI & Automation

      Enhancing Customer Feedback Analysis Through AI Innovations

      August 27, 2025

      Navigating the Impact of AI on SEO and Search Rankings

      August 27, 2025

      5 Automation Hacks Every Home Service Business Needs to Know

      May 3, 2025
    • Finance & Fintech

      Critical Missteps in Finance Marketing: What to Avoid

      August 27, 2025

      Analyzing Future Fintech Marketing Trends: Insights Ahead

      August 27, 2025

      Navigating the Complex Landscape of Finance and Fintech Marketing

      August 27, 2025
    • Legal & Compliance

      Exploring Thought Leadership’s Impact on Legal Marketing

      August 27, 2025

      Maximizing LinkedIn: Strategies for Legal and Compliance Marketing

      August 27, 2025

      Why Transparency Matters in Legal Advertising Practices

      August 27, 2025
    • Medical Marketing

      Enhancing Online Reputation Management in Hospitals: A Guide

      August 27, 2025

      Analyzing Emerging Trends in Health and Medical Marketing

      August 27, 2025

      Exploring Innovative Content Ideas for Wellness Blogs and Clinics

      August 27, 2025
    • E-commerce & Retail

      Strategic Seasonal Campaign Concepts for Online and Retail Markets

      August 27, 2025

      Emerging Trends in E-commerce and Retail Marketing Strategies

      August 27, 2025

      Maximizing Revenue: The Advantages of Affiliate Marketing for E-Commerce

      August 27, 2025
    • Influencer & Community

      Leveraging Influencers: Key Drivers in New Product Launches

      August 27, 2025

      Top Influencer Marketing Platforms to Explore in 2025

      August 27, 2025

      Key Strategies for Successful Influencer Partnership Negotiations

      August 27, 2025
    • Content & Leadership

      The Impact of Social Proof on Thought Leadership Marketing

      August 27, 2025

      Balancing Value-Driven Content and Promotional Messaging Strategies

      August 27, 2025

      Analyzing Storytelling’s Impact on Content Marketing Effectiveness

      August 27, 2025
    • SEO & Analytics

      Scaling Success: Monitoring Indexation of Programmatic SEO Content

      August 27, 2025

      Strategies to Mitigate Duplicate Content in Programmatic SEO

      August 27, 2025

      Effective Data Visualization Techniques for SEO Reporting

      August 27, 2025
    • Marketing Trends

      How Privacy-First Marketing Will Transform the Industry Landscape

      August 27, 2025

      Emerging Trends in Marketing Automation and AI Tools for 2023

      August 27, 2025

      Maximizing ROI: Key Trends in Paid Social Advertising

      August 27, 2025
    Soshace Digital Blog
    Blog / JavaScript / Node.js / Building a Full Stack Application using RedwoodJS
    Node.js

    Building a Full Stack Application using RedwoodJS

    GANESHMANIBy GANESHMANIJuly 1, 2020Updated:July 13, 2024No Comments15 Mins Read
    Facebook Twitter Pinterest Telegram LinkedIn Tumblr Email Reddit
    Building a Full Stack Application using RedwoodJS
    Building a Full Stack Application using RedwoodJS
    Share
    Facebook Twitter LinkedIn Pinterest Email Copy Link
    Building a Full Stack Application using RedwoodJs
    Building a Full Stack Application using RedwoodJs

    This article explains how to build a full-stack application using redwoodjs. we will learn how to build some of the important modules such as authentication, data fetching, and forms in an application using redwoodjs.

    Demo

    Here, we will be building a simple recipe application which contains Authentiation, Data fetching and Forms in it.

    What is RedwoodJS

    RedwoodJS brings the style of JAMstack full-stack application development. if you love to build applications in a JAM stack way. you will fall in love with redwoodJS for sure.

    Redwood helps you to bring your application to the ground as easily as possible. Most of the time, we spend the time setting up a client/server-side architecture rather than writing our business logic. using redwood, we just need to worry about our application business requirement. it helps us to set up and get started within a few commands.

    How it works

    Client and Server-Side in a Mono-Repo Folder Structure
    Client and Server-Side in a Mono-Repo Folder Structure

    Here, we have the client and server-side in a mono-repo folder structure where API stands for backend infrastructure and web stands for the frontend of our application.

    Setup

    Note: I assume that you have installed yarn or node. if not, install nodejs in your machine and make sure it’s installed properly.

    node --version
    yarn --version

    Let’s get started by running the command

    yarn create redwood-app ./recipe-app 
    

    it creates the redwood application scaffold inside the folder recipe-app with all the boilerplate required to run the application.

    you can run the application using the command,

    cd recipe-app
    
    yarn rw dev

    Note: ‘rw’ stands for redwood. you can either use redwood or ‘rw’ in the command line.

    Now, that we have a boilerplate. let’s see the business domains of our application.

    Domain Model

    Domain Model
    Domain Model

    Here, we have user and Recipe domains. an application user story will be like,

    User Story

    1. Users can log in and Signup into the application and view all the recipes created by different users.
    2. Users can create a Recipe and publish it for other people it sees.

    Now, that we know the functionalities of our application. Let’s start by building the authentication system for our application.

    DB Setup

    Now, that we have user stories and domain models. let’s create the DB table. redwood exclusively uses Prisma 2.

    Note: if you’re new Prisma, check out this article

    go to api/prisma/schema.prisma and create the models for User and Recipe and connect Postgres

    datasource DS {
      provider = "postgres"
      url      = "postgresql://postgres:postgres@localhost:5435/postgres"
    }
    generator client {
      provider      = "prisma-client-js"
      binaryTargets = env("BINARY_TARGET")
    }
    
    model User {
      id       Int      @id @default(autoincrement())
      email    String   @unique
      name     String
      password String?
      Recipe   Recipe[]
    }
    model Recipe {
      id          Int    @id @default(autoincrement())
      name        String
      description String
      imageUrl    String
      likes       Int
      userId      Int?
      User        User?  @relation(fields: [userId], references: [id])
    }

    Here, we connect Prisma with Postgres. run the Postgres in your local machine and connect to it using the connection string.

    Here, i am using docker to run the Postgres in local machine. you can read more about this in official docs

    After that, we create models such as User and Recipe with the required attributes for our Postgres tables.

    One important thing to note here is a foreign key reference in the Recipe model. In Prisma, we can declare the reference using @relation field in model along with the fields and references in it.

    Prisma relations Docs

    Once we complete the Prisma model, we can deploy it using the commands,

    yarn rw db save
    

    it saves our changes to the prisma

    yarn rw db up
    

    above command, migrates our changes to postgres database.

    Authentication

    Let’s examine the in-built authentication system that redwood provides us.

    https://redwoodjs.com/tutorial/authentication

    Redwood provides Auth0 and Netlify Auth out of the box. that’s cool. we don’t need to build one for ourselves. But, still, sometimes, we might need to create our own auth system instead of using the third party.

    Actually, there’s a discussion in the redwood community explaining why we don’t need one. checkout the thread.

    I would say, I am exploring this part. it is always good to have options in the implementation. so, I wanted to roll-out my own auth system and see if it really works out.
    spoiler alert: it was not easy and made only possible by a workaround in the frontend part.

    Okay, enough about the talk, let’s see how I built the Auth. let’s start from the backend part.

    For API, we need loginUser  and createUser in the graphql and graphql resolvers. before creating graphql resolvers in our application, let’s see how redwood structures our backend code.

    Structure of Backend Code
    Structure of Backend Code

    let’s explore the directory structure one by one and see the functionalities of it.

    • functions – it contains serverless functions that we need to run to.
    • graphql – it contains all the graphql sdl files of our business domains. for our application, it will contain sdl for user and recipe.
    • lib – we can add the utility functions here. as of now, it contains the Prisma client instance.
    • service – it contains all the graphql resolvers for our application.

    So far, we have seen the structure and its functionalities. let’s create the graphql and resolvers for authentication.

    we can create the sdl and services using the command,

    yarn rw g sdl user
    yarn rw g service user

    the above commands, create sdl and it’s respective service folders for us with all the CRUD logic for the specified domain.

    As of now, our resolvers will contain createUser function which will have the db insert command. but, we can’t directly insert the password into the DB. so, let’s hash it before insert.

    Read More:  React User Login Authentication using useContext and useReducer.

    To do that, we need to install becryptjs and we also need jsonwebtoken to generate jwt

    yarn workspace api add bcryptjs jsonwebtoken
    

    Now, add to functions inside the services/users/users.js 

    export const loginUser = async ({ request }) => {
      try {
        const user = await db.user.findOne({
          where: { email: request.email },
        })
        if (!user) {
          throw new Error('Invalid User')
        }
        const passwordMatch = await bcrypt.compare(request.password, user.password)
        if (!passwordMatch) {
          throw new Error('Invalid Login')
        }
        const token = jwt.sign(
          {
            id: user.id,
            username: user.email,
          },
          'my-secret-from-env-file-in-prod',
          {
            expiresIn: '30d', // token will expire in 30days
          }
        )
        return { user, token }
      } catch (e) {
        return e
      }
    }
    export const createUser = async ({ input }) => {
      const password = await bcrypt.hash(input.password, 10)
      const data = { ...input, password }
      return db.user.create({
        data,
      })
    }

    Other functionality would remain the same. you can test the login and signup functionality in graphql playground.

    Login and Signup Functionality in Graphql Playground
    Login and Signup Functionality in Graphql Playground
    Login and Signup Functionality in Graphql Playground
    Login and Signup Functionality in Graphql Playground

    Now, that we have backend api ready. let’s create the frontend part of it. we can create components using the command,

    yarn rw g page login
    
    yarn rw g page signup

    above commands, creates the page for us along with routes. we just need to edit the page components and we are good to go.

    import {
      Form,
      Label,
      TextField,
      FieldError,
      Submit,
      useMutation,
    } from '@redwoodjs/web'
    import { navigate, routes } from '@redwoodjs/router'
    import { useState } from 'react'
    const LOGIN_USER = gql`
      mutation LoginUser($input: loginUserInput) {
        loginUser(request: $input) {
          token
          user {
            id
            name
            email
          }
        }
      }
    `
    const LoginPage = () => {
      const [state, setState] = useState({
        email: '',
        password: '',
      })
      const [loginUser] = useMutation(LOGIN_USER, {
        onCompleted: ({ loginUser }) => {
          console.log('loginUser', loginUser)
          localStorage.setItem('authToken', loginUser.token)
          setState({ email: '', password: '' })
          setTimeout(() => {
            navigate(routes.home())
          }, 2000)
        },
        onError: (e) => {
          console.log(e)
        },
        ignoreResults: false,
      })
      const onSubmit = () => {
        console.log('on submit', state)
        loginUser({
          variables: {
            input: {
              email: state.email,
              password: state.password,
            },
          },
        })
      }
      const onChange = (e) => {
        setState({ ...state, [e.target.name]: e.target.value })
      }
      return (
        <div className="min-h-screen bg-gray-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
          <div className="sm:mx-auto sm:w-full sm:max-w-md">
            <h2 className="mt-6 text-center text-3xl leading-9 font-extrabold text-gray-900">
              Sign in
            </h2>
          </div>
          <div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
            <div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
              <Form onSubmit={onSubmit}>
                <div>
                  <Label
                    htmlFor="email"
                    className="block text-sm font-medium leading-5 text-gray-700"
                  >
                    Email address
                  </Label>
                  <div className="mt-1 rounded-md shadow-sm">
                    <TextField
                      id="email"
                      type="email"
                      name="email"
                      value={state.email}
                      onChange={onChange}
                      className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md placeholder-gray-400 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 transition duration-150 ease-in-out sm:text-sm sm:leading-5"
                      validation={{
                        required: true,
                        pattern: {
                          value: /[^@]+@[^.]+..+/,
                        },
                      }}
                    />
                  </div>
                  <FieldError name="email" className="text-red-500 text-xs" />
                </div>
                <div className="mt-6">
                  <Label
                    htmlFor="password"
                    className="block text-sm font-medium leading-5 text-gray-700"
                  >
                    Password
                  </Label>
                  <div className="mt-1 rounded-md shadow-sm">
                    <TextField
                      id="password"
                      type="password"
                      name="password"
                      value={state.password}
                      onChange={onChange}
                      className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md placeholder-gray-400 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 transition duration-150 ease-in-out sm:text-sm sm:leading-5"
                      validation={{ required: true }}
                    />
                  </div>
                  <FieldError name="password" className="text-red-500 text-xs" />
                </div>
                <div className="mt-6">
                  <span className="block w-full rounded-md shadow-sm">
                    <Submit className="w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition duration-150 ease-in-out">
                      Sign in
                    </Submit>
                  </span>
                </div>
              </Form>
            </div>
          </div>
          <div className="mx-auto mt-5">
            New User?{' '}
            <a
              className="cursor-pointer text-purple-600"
              onClick={() => {
                navigate(routes.signup())
              }}
            >
              Create An Account
            </a>
          </div>
        </div>
      )
    }
    export default LoginPage

    Here, we have a form on submitting we get the data from the state and call the useMutation from redwood which is a wrapper on apollo graphql react hooks.

    an important thing to note here is, we store the authToken in localStorage to implement the protected routes.  Once login is successful, we redirect the user to home route. redwood provides the routes wrapper too.

    const [loginUser] = useMutation(LOGIN_USER, {
        onCompleted: ({ loginUser }) => {
          console.log('loginUser', loginUser)
          localStorage.setItem('authToken', loginUser.token)
          setState({ email: '', password: '' })
          setTimeout(() => {
            navigate(routes.home())
          }, 2000)
        },
        onError: (e) => {
          console.log(e)
        },
        ignoreResults: false,
      })

    In a same way, create the Signup page component.

    import {
      Form,
      Label,
      TextField,
      FieldError,
      Submit,
      useMutation,
    } from '@redwoodjs/web'
    import { navigate, routes } from '@redwoodjs/router'
    import { useState } from 'react'
    const SINGUP_USER = gql`
      mutation CreateUserMutation($input: CreateUserInput!) {
        createUser(input: $input) {
          id
          name
          email
        }
      }
    `
    const SignupPage = () => {
      const [state, setState] = useState({
        name: '',
        email: '',
        password: '',
      })
      const [signup] = useMutation(SINGUP_USER, {
        onCompleted: (createUser) => {
          localStorage.setItem('authToken', createUser.token)
          setState({ name: '', email: '', password: '' })
          setTimeout(() => {
            navigate(routes.home())
          }, 2000)
        },
      })
      const onSubmit = () => {
        console.log('on submit', state)
        signup({
          variables: {
            input: {
              name: state.name,
              email: state.email,
              password: state.password,
            },
          },
        })
      }
      const onChange = (e) => {
        setState({ ...state, [e.target.name]: e.target.value })
      }
      return (
        <div className="min-h-screen bg-gray-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
          <div className="sm:mx-auto sm:w-full sm:max-w-md">
            <h2 className="mt-6 text-center text-3xl leading-9 font-extrabold text-gray-900">
              Sign Up
            </h2>
          </div>
          <div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
            <div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
              <Form onSubmit={onSubmit}>
                <div>
                  <Label
                    htmlFor="name"
                    className="block text-sm font-medium leading-5 text-gray-700"
                  >
                    Name
                  </Label>
                  <div className="mt-1 rounded-md shadow-sm">
                    <TextField
                      id="name"
                      type="text"
                      name="name"
                      value={state.name}
                      onChange={onChange}
                      className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md placeholder-gray-400 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 transition duration-150 ease-in-out sm:text-sm sm:leading-5"
                      validation={{
                        required: true,
                      }}
                    />
                  </div>
                  <FieldError name="email" className="text-red-500 text-xs" />
                </div>
                <div className="mt-6">
                  <Label
                    htmlFor="email"
                    className="block text-sm font-medium leading-5 text-gray-700"
                  >
                    Email address
                  </Label>
                  <div className="mt-1 rounded-md shadow-sm">
                    <TextField
                      id="email"
                      type="email"
                      name="email"
                      value={state.email}
                      onChange={onChange}
                      className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md placeholder-gray-400 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 transition duration-150 ease-in-out sm:text-sm sm:leading-5"
                      validation={{
                        required: true,
                        pattern: {
                          value: /[^@]+@[^.]+..+/,
                        },
                      }}
                    />
                  </div>
                  <FieldError name="email" className="text-red-500 text-xs" />
                </div>
                <div className="mt-6">
                  <Label
                    htmlFor="password"
                    className="block text-sm font-medium leading-5 text-gray-700"
                  >
                    Password
                  </Label>
                  <div className="mt-1 rounded-md shadow-sm">
                    <TextField
                      id="password"
                      type="password"
                      name="password"
                      value={state.password}
                      onChange={onChange}
                      className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md placeholder-gray-400 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 transition duration-150 ease-in-out sm:text-sm sm:leading-5"
                      validation={{ required: true }}
                    />
                  </div>
                  <FieldError name="password" className="text-red-500 text-xs" />
                </div>
                <div className="mt-6">
                  <span className="block w-full rounded-md shadow-sm">
                    <Submit className="w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition duration-150 ease-in-out">
                      Sign Up
                    </Submit>
                  </span>
                </div>
              </Form>
            </div>
          </div>
          <div className="mx-auto mt-5">
            Already Have an Account?{' '}
            <a
              className="cursor-pointer text-purple-600"
              onClick={() => {
                navigate(routes.login())
              }}
            >
              Login
            </a>
          </div>
        </div>
      )
    }
    export default SignupPage

    Now, that we have a complete authentication system for our application. let see how to create a recipe in application. we might need to implement a form.

    Read More:  Creating Our Own Chat GPT

    Create a Recipe

    so far, we have seen how to build an authentication system. let’s add a functionality to create recipe.

    Backend

    Firstly, let’s add the api for it and then frontend components.

    yarn rw g sdl recipe
    
    yarn rw g service recipe

    change graphql/recipes.sdl.js to add the create recipe and other functionalities in the recipe.

    import gql from 'graphql-tag'
    export const schema = gql`
      type Recipe {
        id: Int!
        name: String!
        description: String!
        imageUrl: String!
        likes: Int!
        userId: Int
        User: User
      }
      type Query {
        recipes: [Recipe!]!
      }
      type Mutation {
        createRecipe(request: CreateRecipeInput): Recipe
        addLike(request: addLikeInput): Recipe
      }
      input addLikeInput {
        recipeId: Int
        likes: Int
      }
      input CreateRecipeInput {
        name: String!
        description: String!
        imageUrl: String!
        likes: Int
        userId: Int
      }
      input UpdateRecipeInput {
        name: String
        description: String
        imageUrl: String
        likes: Int
        userId: Int
      }
    `

    Also, change service/recipes/recipes.js to add the functionalities in resolvers.

    import { db } from 'src/lib/db'
    export const recipes = () => {
      return db.recipe.findMany()
    }
    export const Recipe = {
      user: (_obj, { root }) =>
        db.recipe.findOne({ where: { id: root.id } }).user(),
    }
    export const createRecipe = ({ request }) => {
      console.log('request', request)
      return db.recipe.create({
        data: request,
      })
    }
    export const addLike = ({ request }) => {
      return db.recipe.update({
        data: {
          likes: request.likes,
        },
        where: { id: request.recipeId },
      })
    }

    Frontend

    create a page for recipe using the command,

    yarn rw g page createrecipe
    

    Now, we need to add functionalities for recipe page component.

    import {
      Form,
      Label,
      TextField,
      TextAreaField,
      FieldError,
      Submit,
      useMutation,
    } from '@redwoodjs/web'
    import { navigate, routes } from '@redwoodjs/router'
    import { useState } from 'react'
    import NavbarLayout from '../../layouts/NavbarLayout'
    const CREATE_RECIPE_MUTATION = gql`
      mutation CreateRecipeMutation($input: CreateRecipeInput) {
        createRecipe(request: $input) {
          id
          likes
          name
          description
          likes
          imageUrl
        }
      }
    `
    const CreateRecipePage = () => {
      const [state, setState] = useState({
        name: '',
        description: '',
        imageUrl: '',
        uploadingState: 'NONE',
      })
      const [createRecipe] = useMutation(CREATE_RECIPE_MUTATION, {
        onCompleted: () => {
          navigate(routes.home())
          setState({
            name: '',
            description: '',
            imageUrl: '',
            uploadingState: 'NONE',
          })
        },
      })
      const onSubmit = () => {
        console.log('on submit', state)
        createRecipe({
          variables: {
            input: {
              name: state.name,
              description: state.description,
              imageUrl: state.imageUrl,
              likes: 0,
            },
          },
        })
      }
      const uploadFile = async (e) => {
        console.log('Uploading....')
        setState({ ...state, uploadingState: 'UPLOADING' })
        const files = e.target.files
        const data = new FormData()
        data.append('file', files[0])
        data.append('upload_preset', 'qy3oxqkx')
        const res = await fetch(
          'https://api.cloudinary.com/v1_1/ganeshimaginary/image/upload',
          {
            method: 'POST',
            body: data,
          }
        )
        const file = await res.json()
        setState({
          ...state,
          imageUrl: file.secure_url,
          uploadingState: 'UPLOADED',
        })
      }
      const onChange = (e) => {
        setState({ ...state, [e.target.name]: e.target.value })
      }
      return (
        <NavbarLayout>
          <div className="min-h-screen bg-gray-50 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
            <div className="sm:mx-auto sm:w-full sm:max-w-md">
              <h2 className="mt-6 text-center text-3xl leading-9 font-extrabold text-gray-900">
                Add Recipe
              </h2>
            </div>
            <div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
              <div className="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
                <Form onSubmit={onSubmit}>
                  <div>
                    <Label
                      htmlFor="recipe-name"
                      className="block text-sm font-medium leading-5 text-gray-700"
                    >
                      Recipe Name
                    </Label>
                    <div className="mt-1 rounded-md shadow-sm">
                      <TextField
                        id="name"
                        type="text"
                        name="name"
                        value={state.name}
                        onChange={onChange}
                        className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md placeholder-gray-400 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 transition duration-150 ease-in-out sm:text-sm sm:leading-5"
                        validation={{
                          required: true,
                        }}
                      />
                    </div>
                    <FieldError name="email" className="text-red-500 text-xs" />
                  </div>
                  <div className="mt-6">
                    <Label
                      htmlFor="description"
                      className="block text-sm font-medium leading-5 text-gray-700"
                    >
                      Description
                    </Label>
                    <div className="mt-1 rounded-md shadow-sm">
                      <TextAreaField
                        id="description"
                        name="description"
                        value={state.description}
                        onChange={onChange}
                        className="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md placeholder-gray-400 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 transition duration-150 ease-in-out sm:text-sm sm:leading-5"
                      />
                    </div>
                    <FieldError
                      name="description"
                      className="text-red-500 text-xs"
                    />
                  </div>
                  <input type="file" onChange={uploadFile} />
                  <div className="mt-6">
                    <span className="block w-full rounded-md shadow-sm">
                      <Submit className="w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition duration-150 ease-in-out">
                        Submit
                      </Submit>
                    </span>
                  </div>
                </Form>
              </div>
            </div>
          </div>
        </NavbarLayout>
      )
    }
    export default CreateRecipePage

    Now, that we have add recipe functionality in our application.

    Recipe Form
    Recipe Form

    Data Fetching

    Here we come to the final stage of the tutorial which is fetching the recipes from server and show it to the dashboard.

    Redwood uses a concept called cell which helps the process of data fetching. it provides the state of loading , error and data.

    Main purpose of having cell is separating the functionalities of data fetching outside from the component. so that, we can reuse the cell in other components as well.

    let’s create a recipe cell which fetches the data from the server and show it the home page.

    create a cell using the command,

    yarn rw g cell recipe
    

    After that, we need to add the rendering component based on the recipe data.

    export const QUERY = gql`
      query {
        recipes {
          id
          imageUrl
          name
          description
          likes
        }
      }
    `
    export const Loading = () => <div>Loading...</div>
    export const Empty = () => <div>Empty</div>
    export const Failure = ({ error }) => <div>Error: {error.message}</div>
    export const Success = ({ recipes }) => {
      return recipes.map((recipe, index) => (
        <div
          key={index}
          className="w-1/4 bg-gray-500 md-flex max-w-sm rounded overflow-hidden shadow-lg m-2"
        >
          <img
            className="w-full"
            style={{ maxHeight: '200px' }}
            src={recipe.imageUrl}
            alt="Sunset in the mountains"
          />
          <div className="px-6 py-4">
            <div className="font-bold text-xl mb-2">{recipe.name}</div>
            <p className="text-gray-700 text-base">{recipe.description}</p>
          </div>
          <div className="px-6 py-4">
            <span>
              <i className="fa fa-thumbs-o-up" aria-hidden="true"></i>{' '}
              {recipe.likes}
            </span>
          </div>
        </div>
      ))
    }

    Conclusion

    We learned how to build a full-stack application using redwoodjs. I hope this article covered some of the important modules for building a full-stack application. Note that, redwood is still under development and not ready for production yet. So,  learn the concept of redwood and keep an eye on it. it can become a new way of building a full-stack application.

    Complete source code can be found here

    Share. Facebook Twitter Pinterest LinkedIn Tumblr Email
    GANESHMANI

      Related Posts

      An Introduction to Clustering in Node.js

      March 26, 2024

      JAMstack Architecture with Next.js

      March 15, 2024

      Rendering Patterns: Static and Dynamic Rendering in Nextjs

      March 7, 2024
      Leave A Reply Cancel Reply

      You must be logged in to post a comment.

      Stay In Touch
      • Facebook
      • Twitter
      • Pinterest
      • Instagram
      • YouTube
      • Vimeo
      Don't Miss
      Programming November 24, 2024

      Essential Steps for Beginners in Mobile App Development

      Embarking on mobile app development requires a systematic approach. Begin by defining your app’s purpose and target audience. Choose a suitable platform (iOS, Android) and programming language (Swift, Kotlin). Familiarize yourself with development tools and frameworks, then prototype and iterate.

      Optimizing Graphql Data Queries with Data Loader

      January 8, 2021

      React Lesson 9: Homework Lesson 8

      February 24, 2020

      How We are Looking for Proposals on UpWork

      January 8, 2016

      Categories

      • AI & Automation
      • Angular
      • ASP.NET
      • AWS
      • B2B Leads
      • Beginners
      • Blogs
      • Business Growth
      • Case Studies
      • Comics
      • Consultation
      • Content & Leadership
      • CSS
      • Development
      • Django
      • E-commerce & Retail
      • Entrepreneurs
      • Entrepreneurship
      • Events
      • Express.js
      • Facebook Ads
      • Finance & Fintech
      • Flask
      • Flutter
      • Franchising
      • Funnel Strategy
      • Git
      • GraphQL
      • Home Services Marketing
      • Influencer & Community
      • Interview
      • Java
      • Java Spring
      • JavaScript
      • Job
      • Laravel
      • Lead Generation
      • Legal & Compliance
      • LinkedIn
      • Machine Learning
      • Marketing Trends
      • Medical Marketing
      • MSP Lead Generation
      • MSP Marketing
      • NestJS
      • Next.js
      • Node.js
      • Node.js Lessons
      • Paid Advertising
      • PHP
      • Podcasts
      • POS Tutorial
      • Programming
      • Programming
      • Python
      • React
      • React Lessons
      • React Native
      • React Native Lessons
      • Recruitment
      • Remote Job
      • SaaS & Tech
      • SEO & Analytics
      • Soshace
      • Startups
      • Swarm Intelligence
      • Tips
      • Trends
      • Vue
      • Wiki
      • WordPress
      Top Posts

      Soshace Becomes an Informational Partner of JS Nation in Amsterdam

      Events May 17, 2019

      Hot Topics in Web Development | 12 Hottest Trends for 2019

      Trends March 6, 2019

      Nodejs Lesson 15: Internals of Nodejs: LibUV

      Node.js February 4, 2021

      Analyzing Leadership Styles and Their Influence on Project Success

      JavaScript November 25, 2024

      Subscribe to Updates

      Get The Latest News, Updates, And Amazing Offers

      About Us
      About Us

      Soshace Digital delivers comprehensive web design and development solutions tailored to your business objectives. Your website will be meticulously designed and developed by our team of seasoned professionals, who combine creative expertise with technical excellence to transform your vision into a high-impact, user-centric digital experience that elevates your brand and drives measurable results.

      7901 4th St N, Suite 28690
      Saint Petersburg, FL 33702-4305
      Phone: 1(877)SOSHACE

      Facebook X (Twitter) Instagram Pinterest YouTube LinkedIn
      Our Picks
      Programming

      24. Уроки Node.js. Чтение параметров из командной строки и окружения.

      Legal & Compliance

      Exploring Thought Leadership’s Impact on Legal Marketing

      Startups

      Strategies for Transforming Your Startup into a Profitable Venture

      Most Popular

      Maximizing LinkedIn Engagement: Leveraging Video Content

      LinkedIn

      Google I/O 2019: New JavaScript Features

      Events

      Writing end-to-end tests for Nuxt apps using jsdom and AVA

      JavaScript
      © 2025 Soshace Digital.
      • Home
      • About
      • Services
      • Contact Us
      • Privacy Policy
      • Terms & Conditions

      Type above and press Enter to search. Press Esc to cancel.