These two files work together. First, imagine a scenario where you are working with a partner on a project. You are working on a Linux computer, which you’ve configured for this kind of project, and it has all of the most up-to-date version of everything. Your partner is on a Windows computer, and when they install the tools that you tell them you are using, they get slightly different versions of the packages. Later, something works when you try it, so you share it with your partner, and they tell you later that it is broken! After hours debugging, you discover that since their versions are different, the stuff you implemented isn’t supported.
That is where package.json comes in - if you have worked with Python virtual environments, you’ll recognize this as Node.js’s way of creating a virtual environment. Whenever you install a tool or dependency, it is added to package.json, and running ‘npm install’ reads package.json for everything it should instead. That way, when your collaborator runs npm install, you can be certain they they get the exact same packages that you do. There are other things you can do in package.json, which you’ll learn about as you need them in other projects, but that is its primary service to our app.
package-lock.json takes it one step further - where package.json saves the current tools you are using, bugfixes and minor version updates may vary between updates. package-lock.json keeps a record of the changes to package.json, describing the exact tree that is generated any time you make a change, to ensure that installs use precisely the same versions. You can read more about how these two files work together here, but functionally, it suffices to know that you don’t have to do anything to them manually - npm takes care of them for you.