DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • How to Effortlessly Host Your Angular Website on GitHub Pages
  • T3 vs. T4 Stack: Which Starter Kit To Choose?
  • Deploy MuleSoft App to CloudHub2 Using GitHub Actions CI/CD Pipeline
  • Top React Libraries for Data-Driven Dashboard App Development

Trending

  • How the Go Runtime Preempts Goroutines for Efficient Concurrency
  • Building Enterprise-Ready Landing Zones: Beyond the Initial Setup
  • Integration Isn’t a Task — It’s an Architectural Discipline
  • Vibe Coding With GitHub Copilot: Optimizing API Performance in Fintech Microservices
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Deployment
  4. Github Package Search App Using Next.js

Github Package Search App Using Next.js

Develop a simple search app where we get all the information required to choose the package in one shot.

By 
Nagappan Subramanian user avatar
Nagappan Subramanian
DZone Core CORE ·
Aug. 29, 22 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
4.3K Views

Join the DZone community and get the full member experience.

Join For Free

Every day as part of the development, we search for open source packages in Github using google. It will be time taking process as we need to search for the keywords, get into each Github repository to check the below items to choose the packages:

  • Is it completely open source to change it or use for the commercial app?
  • Whether it is not archived or is some active community working for it? 
  • Does it have a lot of issues? 
  • What are the languages used in this repository? 
  • How much is the size of the package, so it doesn't make my project bulkier?
  • When did the recent update happen? 

I want to develop a simple search app where we get all the information required to choose the package in one shot. It will also help any development team as a handy tool to choose the Github packages. 

Github provides a search API to search the repository for the search term, and for each repo, get the details information like branches, tags, and contributors and show it as a single view. We choose Next.js so we can search in the backend and render only the contents on the client side. It provides a free deployment tool to deploy and is available publicly for use. 

Head's Up of Next.js

Next.js is a server-side rendering of the react.js framework, which supports us in creating the single-page web application. It has everything required to develop production-ready web applications - production SSR Web UI applications: Hybrid static & server rendering, TypeScript support, Smart bundling, Route pre-fetching, and more. To know more about Next.js, please check their official site.

Project Setup

The pre-requisite is to have node.js 12.2.0 or later.

We can create the next app with TypeScript support by a simple command:

npx create-next-app@latest --ts

To get started in an existing project, create an empty tsconfig.json file in the root folder:

touch tsconfig.json

Project Start

Then, run next (normally npm run dev or yarn dev), and Next.js will guide you through the installation of the required packages to finish the setup:

npm run dev


Install Chakra UI 

I used Chakra UI to get ready to use components quicker for building the UI. It provides Box and Stack layout components, which is easy to style your components through props.

npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^6


Search App:

The motive is to provide search keywords and get the list of repositories available in Github with all the essential information like below:

Search the Github Repository


Next.js app starts with index.tsx page. In that, I created the server-side props function, which will be responsible for the server call to fetch the data for the page before the page comes to the client. In this function, we have used Github search repositories API. Search text is given in the UI, and the response is extracted with the required information into the repoItem model.


JavaScript
 
export const getServerSideProps = async ({
  query,
}: GetServerSidePropsContext<QueryParams>): Promise<GetStaticPropsResult<ContentPageProps>> => {
  console.log("context.query :>> ", query);
 
  if (!query || !query?.q) {
    return { props: { repos: [], searchText: "", count: 0 } };
  }
  const searchText: string = `${query.q}`;
  const url = `https://api.github.com/search/repositories?q=${query?.q}`;
  const token: RequestInit = {
    headers: [
      ["Authorization", `${process.env.token}`],
      ["Accept", "application/vnd.github.v3+json"]
    ],
    method: "GET",
  };
  const res = await fetch(url, token);
  const data = await res.json();
  // console.log('data :>> ', data);
  const repos: RepoItem[] = data.items.map((item: any) => {
    const repo = {
      id: item.id,
      language: item.language,
      name: item.name,
      full_name: item.full_name,
      description: item.description,
      stars: item.stargazers_count,
      watchers: item.watchers_count,
      forks: item.forks,
      homepage: item.homepage,
      topics: item.topics,
      tags: item.tags_url,
      size:item.size,
      issue: item.open_issues_count,
      contributors: item.contributors_url,
      archived: item.archived,
      visibility: item.visibility,
      updated_at: item.updated_at,
      license: item.license,
      owner: item.owner,
      has_wiki: item.has_wiki
    };
    return repo;
  });
  const results = { repos: repos, searchText: searchText, count: data.total_count || 0 };
  return { props: results };
};


Search Box is a form component where we allow the user to enter the search text, and on submitting the form, it hits the backend with the query string. The backend is the serversideprops which will, in turn, trigger the Github API to get the matched repositories for the search keyword(as explained above).  

One thing to be noted is that I didn't use onchange as it will trigger on each keypress. Instead, I used the useRef, to get the input value when submitting the button.

Search Results will be displayed using the custom component - RepoContainer for each RepoItem data under the VStack layout component.

JavaScript
 
<VStack>
  <Spacer />
  {repos && repos.map((repoItem: RepoItem) => <RepoContainer key={repoItem.id} repo={repoItem} />)}
</VStack>

Each RepoItem consists of four stack components marked by comments in the below code snippet. First stack component consists of repository name, full name, and language details. In the second stack, meta details like the tags, branches, size of package, license, and owner are kept. The third stack, how the repository is stable and actively updated, and fourth stack shows the popularity of the repository community.

JavaScript
 
<Box
      color={useColorModeValue("gray.700", "gray.200")}
      w={"100%"}
      borderRadius={10}
      bg="#B1F1F3"
    >
      <Container as={Stack} maxW={"9xl"} py={10}>
        <SimpleGrid templateColumns={{ sm: "1fr 1fr", md: "2fr 2fr 2fr 2fr" }} spacing={3}>
          <Stack>{/* First stack component */}
            <ListHeader>{repo?.name}</ListHeader>
            <Text fontSize={"sm"}>
              <NextLink href={`https://github.com/${repo?.full_name}`} passHref>
                <Link color="blue">{repo?.full_name}</Link>
              </NextLink>
              </Text>
            <Stack direction={"row"} mt={10}>
              <Text fontSize={"sm"}>
                Language:{" "}
                <Badge colorScheme="red" variant="solid">
                  {repo.language}
                </Badge>
              </Text>
            </Stack>
            <Stack direction={"row"}>frameworks :</Stack>
          </Stack>

          <Stack align={"flex-start"} fontSize={"sm"}>{/* Second stack component */}
            <Stack direction={"row"} align={"center"}>
              {/* <Text>Releases  </Text> */}
              <GoTag />  <Tags url={repo.tags} />
              <GoGitBranch /> <Branches repo={repoName} /> 
            </Stack>
            <Text>Size of the package : {Math.round((repo.size / 32768) * 100) / 100} MB </Text>
            <Text>License : {repo.license?.name ? repo.license?.name:"None"}  </Text>
            <Text>Owner : <Avatar src={repo?.owner?.avatar_url} size='xs' /> {repo?.owner?.login}</Text>
          </Stack>
          <Stack align={"flex-start"} fontSize={"sm"}>{/* Third stack component */}
            <Stack direction={"row"} align={"center"}>
              <GoIssueOpened /> <Text>Issues :{repo.issue} |</Text>
              <GoOrganization /> <Contributors url={repo.contributors} icon={<GoOrganization />} />
            </Stack>
            <Stack direction={"row"} align={"center"}>
              <GoArchive />
              <Text style={{ color: repo.archived ? 'red': 'black'}}> IsArchived: {repo.archived ? "true" : "false"} |</Text>
              <GoEye />
              <Text> Visibility: {repo.visibility}</Text>
            </Stack>
            <Stack direction={"row"} align={"center"}>
              <GoWatch /> <Text>Last Updated : {updatedTime}</Text>
            </Stack>
            <Stack direction={"row"} align={"center"}>
              <GoGitPullRequest />  <PR repo={repoName} />
            </Stack>
          </Stack>
          <Stack align={"flex-start"}>{/* Fourth stack component */}
            <Stack direction={"row"} align={"center"}>
              <GoStar /> <Text>{repo.stars} |</Text> <GoRepoForked /> <Text>{repo.forks} |</Text>
              <GoEye /> <Text>{repo.watchers} </Text>
            </Stack>
            <NextLink href={`${repo.homepage}`} passHref>
              <Link color="blue">Homepage</Link>
            </NextLink>
            {/* <Text>Examples</Text> */}
            {repo.has_wiki ? (
              <NextLink href={`https://github.com/${repo?.full_name}/wiki`} passHref>
                <Link color="blue">Wiki Link</Link>
              </NextLink>
            ): ""}
          </Stack>
        </SimpleGrid>
        <SimpleGrid>{/* Repo description */}
          <Text>Description :{repo.description}</Text>
        </SimpleGrid>
        <SimpleGrid>{/* Repo Topics */}
          <Stack direction={"row"} align={"center"}>
            <Wrap>
              {repo.topics &&
                repo.topics.map((res: string) => (
                  <>
                    <WrapItem key={res}>
                      <Badge variant="solid" colorScheme="messenger" >
                        {res}
                      </Badge>
                    </WrapItem>
                  </>
                ))}
            </Wrap>
          </Stack>
        </SimpleGrid>
      </Container>
    </Box>

At last, the description and Github topics are kept to get a glimpse of the project. 

If you keenly note, there are components like Branches, and Prs, which react custom components. It will be rendered asynchronously once the repo container is loaded. 

Let's take a closer look at the PR component; it returns a div tag with pr counts. PR counts are not directly available in the search repositories call. There is one more call to be made to get the pr counts, so we use the API routes. This API route, in turn, calls the Github API with the token and gets the count of pull requests. This count computation load has been delegated to the backend.

Here reponame is passed through props to the component, and the count state is resolved after the fetch API call.

JavaScript
 
interface propTypes {
  repo: string;
}

function PR(props: propTypes) {
  const { repo } = props;
  const [count, setCount] = useState(0);
  useEffect(() => {
      if (repo) {
        fetch(`/api/prcounts?repo=${repo}`).then((res) => res.json()).then(res => {
          const prcount = res.data;
          setCount(prcount);
        });
      } else {
        setCount(0);
      }
  }, [repo])
  return (
    <div className='center'>   Pull Request : {count}</div>
  )
}

export default PR


In the pages/api/pr.ts file, we invoke the Github API call, get the pull requests, and find the count of pull requests. Then the pull request value is returned in JSON format like {data: prcount}to the frontend.


TypeScript
 
type ResponseData = {
  data: string
}

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<ResponseData>
) {
    const repoName: string = `${req.query.repo}`;
    const url = new URL(`https://api.github.com/repos/${repoName}/pulls`);
    const options = { 'headers' : {
        'Authorization': process.env.token,
        'Accept': "application/vnd.github.v3+json",
        'User-Agent': 'Mozilla/5.0' 
     },
    'method': 'GET',
    };

    const clientreq: ClientRequest = https.request(url, options, (apiresponse: IncomingMessage) => {
        let respJson: string = "";

        apiresponse.on('data', (chunk: string) => { 
          respJson += chunk;
        });

        apiresponse.on('end', () => {
          if (apiresponse.statusCode === 200) {
            let prResponses = JSON.parse(respJson);
            const prCounts = prResponses.length;
            res.status(200).json({'data': prCounts});
          }
        })
    });

    clientreq.on('error', (e: Error) => {
        console.error('error message', e.message);
    });
    clientreq.end();
}


I made the same pattern for branches, and contributors, like a react component that invoked API routes to fetch the data from Github APIs. 

Complete source code is available for reference.

Deployed for usage.

My plan is to expand this app and get more information like test build status, demo page, and remove fork duplicates in the search results. Eager to get feedback from you as well, like whether you need to see more information or some more options in terms of searching the package. Please drop in the below comments sections.

GitHub Next.js app

Opinions expressed by DZone contributors are their own.

Related

  • How to Effortlessly Host Your Angular Website on GitHub Pages
  • T3 vs. T4 Stack: Which Starter Kit To Choose?
  • Deploy MuleSoft App to CloudHub2 Using GitHub Actions CI/CD Pipeline
  • Top React Libraries for Data-Driven Dashboard App Development

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!